1
0
Fork 1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-07-07 06:43:20 -04:00
pkg-proxy/internal/server/dashboard.go
Andrew Nesbitt da4fa2fc98
Add PROXY_UI_URL for separate UI/package URL advertisement (#161)
Adds UIBaseURL (env PROXY_UI_URL), advertised separately from BaseURL
for deployments where the UI is reached on a public domain while build
machines hit a Docker network alias for the package endpoints. Defaults
to BaseURL when unset.

Consumers:
- Logged alongside base_url at startup.
- <link rel="canonical"> and og:url / og:title / og:site_name in every
  UI page, omitted when UIBaseURL is empty.
- Banner on the install guide when UIBaseURL differs from BaseURL,
  clarifying that the URLs in the snippets are the package endpoint and
  that the UI itself lives elsewhere.

Docs call out that the proxy serves UI and package endpoints on the same
listener, so reverse proxies fronting the UI publicly must restrict the
public route to PathPrefix(/ui) to avoid exposing /npm, /pypi, etc.
nginx and Traefik examples both show the path-split pattern.
2026-06-07 16:12:43 +01:00

448 lines
12 KiB
Go

package server
import (
"html/template"
"github.com/git-pkgs/proxy/internal/database"
)
// DashboardData contains data for rendering the dashboard.
type DashboardData struct {
Layout
Stats DashboardStats
EnrichmentStats EnrichmentStatsView
RecentPackages []PackageInfo
PopularPackages []PackageInfo
}
// DashboardStats contains cache statistics for the dashboard.
type DashboardStats struct {
CachedArtifacts int64
TotalSize string
TotalPackages int64
TotalVersions int64
}
// EnrichmentStatsView contains enrichment statistics for display.
type EnrichmentStatsView struct {
EnrichedPackages int64
VulnSyncedPackages int64
TotalVulnerabilities int64
CriticalVulns int64
HighVulns int64
MediumVulns int64
LowVulns int64
HasVulns bool
}
// PackageInfo contains information about a cached package.
type PackageInfo struct {
Ecosystem string
Name string
Version string
Size string
Hits int64
CachedAt string
License string
LicenseCategory string
VulnCount int64
LatestVersion string
IsOutdated bool
}
// RegistryConfig contains configuration instructions for a package registry.
type RegistryConfig struct {
ID string
Name string
Language string
Endpoint string
Instructions template.HTML
}
// PackageShowData contains data for rendering the package show page.
type PackageShowData struct {
Layout
Package *database.Package
Versions []database.Version
Vulnerabilities []database.Vulnerability
LicenseCategory string
}
// VersionShowData contains data for rendering the version show page.
type VersionShowData struct {
Layout
Package *database.Package
Version *database.Version
Artifacts []database.Artifact
Vulnerabilities []database.Vulnerability
IsOutdated bool
LicenseCategory string
HasCachedArtifact bool
}
// SearchPageData contains data for rendering the search results page.
type SearchPageData struct {
Layout
Query string
Ecosystem string
Results []SearchResultItem
Count int
Page int
PerPage int
TotalPages int
}
// SearchResultItem represents a single search result for display.
type SearchResultItem struct {
Ecosystem string
Name string
LatestVersion string
License string
LicenseCategory string
Hits int64
Size int64
SizeFormatted string
CachedAt string
VulnCount int64
}
// PackagesListPageData contains data for rendering the packages list page.
type PackagesListPageData struct {
Layout
Ecosystem string
SortBy string
Results []SearchResultItem
Count int
Page int
PerPage int
TotalPages int
}
func supportedEcosystems() []string {
// this list should be kept sorted in lexicographic order so
// that the 'select' list in the UI will be in the expected
// order
return []string{
"cargo",
"composer",
"conan",
"conda",
"cran",
"deb",
"gem",
"golang",
"hex",
"julia",
"maven",
"npm",
"nuget",
"oci",
"pub",
"pypi",
"rpm",
}
}
func ecosystemBadgeLabel(ecosystem string) string {
switch ecosystem {
case "oci":
return "container"
case "deb":
return "debian"
default:
return ecosystem
}
}
func ecosystemBadgeClasses(ecosystem string) string {
base := "inline-flex items-center px-2 py-0.5 rounded text-xs font-medium"
switch ecosystem {
case "npm", "maven":
return base + " bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300"
case "cargo":
return base + " bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300"
case "gem":
return base + " bg-pink-100 text-pink-700 dark:bg-pink-900/50 dark:text-pink-300"
case "go":
return base + " bg-cyan-100 text-cyan-700 dark:bg-cyan-900/50 dark:text-cyan-300"
case "hex":
return base + " bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300"
case "pub":
return base + " bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"
case "pypi":
return base + " bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-300"
case "nuget":
return base + " bg-indigo-100 text-indigo-700 dark:bg-indigo-900/50 dark:text-indigo-300"
case "composer":
return base + " bg-violet-100 text-violet-700 dark:bg-violet-900/50 dark:text-violet-300"
case "conan":
return base + " bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-300"
case "conda":
return base + " bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300"
case "cran":
return base + " bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300"
case "julia":
return base + " bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-300"
case "oci":
return base + " bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-300"
case "deb":
return base + " bg-red-100 text-red-800 dark:bg-red-900/50 dark:text-red-300"
case "rpm":
return base + " bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-300"
default:
return base + " bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300"
}
}
func getRegistryConfigs(baseURL string) []RegistryConfig {
return []RegistryConfig{
{
ID: "npm",
Name: "npm",
Language: "JavaScript",
Endpoint: "/npm/",
Instructions: template.HTML(`<p class="config-note">Configure npm to use the proxy:</p>
<pre><code># In ~/.npmrc or project .npmrc
registry=` + baseURL + `/npm/
# Or via environment variable
npm_config_registry=` + baseURL + `/npm/ npm install</code></pre>`),
},
{
ID: "cargo",
Name: "Cargo",
Language: "Rust",
Endpoint: "/cargo/",
Instructions: template.HTML(`<p class="config-note">Configure Cargo to use the proxy (sparse index protocol):</p>
<pre><code># In ~/.cargo/config.toml or project .cargo/config.toml
[source.crates-io]
replace-with = "proxy"
[source.proxy]
registry = "sparse+` + baseURL + `/cargo/"</code></pre>`),
},
{
ID: "gem",
Name: "RubyGems",
Language: "Ruby",
Endpoint: "/gem/",
Instructions: template.HTML(`<p class="config-note">Configure Bundler/RubyGems to use the proxy:</p>
<pre><code># In Gemfile
source "` + baseURL + `/gem"
# Or configure globally
gem sources --add ` + baseURL + `/gem/
bundle config mirror.https://rubygems.org ` + baseURL + `/gem</code></pre>`),
},
{
ID: "go",
Name: "Go Modules",
Language: "Go",
Endpoint: "/go/",
Instructions: template.HTML(`<p class="config-note">Set the GOPROXY environment variable:</p>
<pre><code>export GOPROXY=` + baseURL + `/go,direct
# Add to your shell profile for persistence</code></pre>`),
},
{
ID: "hex",
Name: "Hex",
Language: "Elixir",
Endpoint: "/hex/",
Instructions: template.HTML(`<p class="config-note">Configure Hex to use the proxy:</p>
<pre><code># In ~/.hex/hex.config
{default_url, &lt;&lt;"` + baseURL + `/hex"&gt;&gt;}.
# Or via environment variable
export HEX_MIRROR=` + baseURL + `/hex</code></pre>`),
},
{
ID: "pub",
Name: "pub.dev",
Language: "Dart/Flutter",
Endpoint: "/pub/",
Instructions: template.HTML(`<p class="config-note">Set the PUB_HOSTED_URL environment variable:</p>
<pre><code>export PUB_HOSTED_URL=` + baseURL + `/pub</code></pre>`),
},
{
ID: "pypi",
Name: "PyPI",
Language: "Python",
Endpoint: "/pypi/",
Instructions: template.HTML(`<p class="config-note">Configure pip to use the proxy:</p>
<pre><code># Via command line
pip install --index-url ` + baseURL + `/pypi/simple/ package_name
# In ~/.pip/pip.conf
[global]
index-url = ` + baseURL + `/pypi/simple/</code></pre>`),
},
{
ID: "maven",
Name: "Maven",
Language: "Java",
Endpoint: "/maven/",
Instructions: template.HTML(`<p class="config-note">Configure Maven to use the proxy:</p>
<pre><code>&lt;!-- In ~/.m2/settings.xml --&gt;
&lt;settings&gt;
&lt;mirrors&gt;
&lt;mirror&gt;
&lt;id&gt;proxy&lt;/id&gt;
&lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
&lt;url&gt;` + baseURL + `/maven/&lt;/url&gt;
&lt;/mirror&gt;
&lt;/mirrors&gt;
&lt;/settings&gt;</code></pre>`),
},
{
ID: "gradle",
Name: "Gradle Build Cache",
Language: "Java/Kotlin",
Endpoint: "/gradle/",
Instructions: template.HTML(`<p class="config-note">Configure Gradle to use the proxy for HttpBuildCache:</p>
<pre><code>// In settings.gradle(.kts)
buildCache {
remote&lt;HttpBuildCache&gt; {
url = uri("` + baseURL + `/gradle/")
push = true
}
}</code></pre>`),
},
{
ID: "nuget",
Name: "NuGet",
Language: ".NET",
Endpoint: "/nuget/",
Instructions: template.HTML(`<p class="config-note">Configure NuGet to use the proxy:</p>
<pre><code>&lt;!-- In nuget.config --&gt;
&lt;configuration&gt;
&lt;packageSources&gt;
&lt;clear /&gt;
&lt;add key="proxy" value="` + baseURL + `/nuget/v3/index.json" /&gt;
&lt;/packageSources&gt;
&lt;/configuration&gt;
# Or via CLI
dotnet nuget add source ` + baseURL + `/nuget/v3/index.json -n proxy</code></pre>`),
},
{
ID: "composer",
Name: "Composer",
Language: "PHP",
Endpoint: "/composer/",
Instructions: template.HTML(`<p class="config-note">Configure Composer to use the proxy:</p>
<pre><code>// In composer.json
{
"repositories": [
{
"type": "composer",
"url": "` + baseURL + `/composer"
}
]
}
# Or globally
composer config -g repositories.proxy composer ` + baseURL + `/composer</code></pre>`),
},
{
ID: "conan",
Name: "Conan",
Language: "C/C++",
Endpoint: "/conan/",
Instructions: template.HTML(`<p class="config-note">Configure Conan to use the proxy:</p>
<pre><code>conan remote add proxy ` + baseURL + `/conan
conan remote disable conancenter</code></pre>`),
},
{
ID: "conda",
Name: "Conda",
Language: "Python/R",
Endpoint: "/conda/",
Instructions: template.HTML(`<p class="config-note">Configure Conda to use the proxy:</p>
<pre><code># In ~/.condarc
channels:
- ` + baseURL + `/conda/main
- ` + baseURL + `/conda/conda-forge
default_channels:
- ` + baseURL + `/conda/main
# Or via command
conda config --add channels ` + baseURL + `/conda/main</code></pre>`),
},
{
ID: "cran",
Name: "CRAN",
Language: "R",
Endpoint: "/cran/",
Instructions: template.HTML(`<p class="config-note">Configure R to use the proxy:</p>
<pre><code># In R session
options(repos = c(CRAN = "` + baseURL + `/cran"))
# In ~/.Rprofile for persistence
local({
r &lt;- getOption("repos")
r["CRAN"] &lt;- "` + baseURL + `/cran"
options(repos = r)
})</code></pre>`),
},
{
ID: "julia",
Name: "Julia",
Language: "Julia",
Endpoint: "/julia/",
Instructions: template.HTML(`<p class="config-note">Set the Pkg server before starting Julia:</p>
<pre><code>export JULIA_PKG_SERVER=` + baseURL + `/julia</code></pre>
<p class="config-note">Or inside a running session:</p>
<pre><code>ENV["JULIA_PKG_SERVER"] = "` + baseURL + `/julia"
using Pkg; Pkg.update()</code></pre>`),
},
{
ID: "oci",
Name: "Container Registry",
Language: "Docker/OCI",
Endpoint: "/v2/",
Instructions: template.HTML(`<p class="config-note">Configure Docker to use the proxy as a mirror:</p>
<pre><code># In /etc/docker/daemon.json
{
"registry-mirrors": ["` + baseURL + `"]
}
# Then restart Docker
sudo systemctl restart docker
# Or pull directly
docker pull ` + baseURL[8:] + `/library/nginx:latest</code></pre>`),
},
{
ID: "deb",
Name: "Debian/APT",
Language: "Debian/Ubuntu",
Endpoint: "/debian/",
Instructions: template.HTML(`<p class="config-note">Configure APT to use the proxy:</p>
<pre><code># In /etc/apt/sources.list or /etc/apt/sources.list.d/proxy.list
deb ` + baseURL + `/debian stable main contrib
# Replace your existing sources.list entries with the proxy URL
# Then run:
sudo apt update</code></pre>`),
},
{
ID: "rpm",
Name: "RPM/Yum",
Language: "Fedora/RHEL",
Endpoint: "/rpm/",
Instructions: template.HTML(`<p class="config-note">Configure yum/dnf to use the proxy:</p>
<pre><code># In /etc/yum.repos.d/proxy.repo
[proxy-fedora]
name=Fedora via Proxy
baseurl=` + baseURL + `/rpm/releases/$releasever/Everything/$basearch/os/
enabled=1
gpgcheck=0
# Then run:
sudo dnf clean all
sudo dnf update</code></pre>`),
},
}
}