mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-18 21:26:56 +00:00
fix(admin): use protocol-relative URLs for component links so HTTPS clusters don't break clicks (#9256)
* fix(admin): use protocol-relative URLs for component links Hardcoded http:// in admin UI templates breaks browser-initiated clicks to master / volume / filer / EC shard / Iceberg REST URLs whenever the target component runs HTTPS-only via security.toml [https.X] sections. The browser sends plain HTTP to a TLS-only endpoint and gets 400 "client sent an HTTP request to an HTTPS server". Same root pattern as #9227 (admin's own backend /dir/status fetch); this PR is the browser-facing equivalent. Replace fmt.Sprintf("http://%s...") with fmt.Sprintf("//%s...") and the JS-string '<a href="http://' with '<a href="//' so the browser uses the same scheme as the page hosting the link. Backwards compatible: - HTTPS-only deployments: links now work - HTTP-only deployments: identical behavior to before - Mixed: edge case, addressed by future per-component public-URL work Affected templates (9 files), each kept in lockstep with its generated _templ.go sibling so reviewers don't need to run templ generate: - weed/admin/view/app/admin.templ - weed/admin/view/app/cluster_filers.templ - weed/admin/view/app/cluster_masters.templ (Go templ + JS modal) - weed/admin/view/app/cluster_volume_servers.templ (Go templ + JS modal) - weed/admin/view/app/cluster_volumes.templ - weed/admin/view/app/ec_volume_details.templ - weed/admin/view/app/volume_details.templ - weed/admin/view/app/iceberg_catalog.templ - weed/admin/view/app/s3tables_buckets.templ 17 link constructions total, +32/-32 lines. * fix(admin): protocol-relative URLs in iceberg + s3tables JS overrides Per Gemini code review on this PR: the JS scripts in iceberg_catalog and s3tables_buckets templates overwrite the href attribute of the "Open Iceberg REST" links after page load, replacing the protocol-relative URL set by the templ render with a hardcoded http://<host>:<port>/v1/config. Apply the same protocol-relative fix to the JS template literals so they don't undo the templ-side change. Browser uses the page scheme (http or https) to fill in the protocol. Mirrored in iceberg_catalog_templ.go and s3tables_buckets_templ.go. * fix(admin): displayed Iceberg endpoint scheme follows page protocol Per CodeRabbit review on this PR: the on-page guidance text in iceberg and s3tables templates still showed a literal `http://` even after the clickable link was switched to a protocol-relative URL. In HTTPS-only deployments operators see `http://host:8181/v1` as the suggested endpoint, copy it, and get a broken connection. Wrap the scheme in <span id="iceberg-protocol"> (and the s3tables counterpart) and have the existing inline script set its innerText to window.location.protocol minus the trailing colon. Same pattern as the existing dynamic host substitution. Mirrored in *_templ.go so reviewers do not need templ generate. SQL/JSON code-block examples (CREATE EXTERNAL TABLE ... ENDPOINT 'http://...', "uri": "http://..." ) are intentionally left as-is — they are starter snippets users adapt to their environment, not clickable or copy-paste-into-runtime values. Happy to follow up with server-side scheme threading if requested.
This commit is contained in:
@@ -173,7 +173,7 @@ templ Admin(data dash.AdminData) {
|
||||
for _, master := range data.MasterNodes {
|
||||
<tr>
|
||||
<td>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", master.Address))} target="_blank">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", master.Address))} target="_blank">
|
||||
{master.Address}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -280,7 +280,7 @@ templ Admin(data dash.AdminData) {
|
||||
<tr>
|
||||
<td>{vs.ID}</td>
|
||||
<td>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", vs.GetDisplayAddress()))} target="_blank">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", vs.GetDisplayAddress()))} target="_blank">
|
||||
{vs.GetDisplayAddress()}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -358,7 +358,7 @@ templ Admin(data dash.AdminData) {
|
||||
for _, filer := range data.FilerNodes {
|
||||
<tr>
|
||||
<td>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s", filer.Address))} target="_blank">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s", filer.Address))} target="_blank">
|
||||
{filer.Address}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
|
||||
@@ -135,7 +135,7 @@ func Admin(data dash.AdminData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 templ.SafeURL
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", master.Address)))
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", master.Address)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/admin.templ`, Line: 176, Col: 126}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func Admin(data dash.AdminData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 templ.SafeURL
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", vs.GetDisplayAddress())))
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", vs.GetDisplayAddress())))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/admin.templ`, Line: 283, Col: 134}
|
||||
}
|
||||
@@ -470,7 +470,7 @@ func Admin(data dash.AdminData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 templ.SafeURL
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s", filer.Address)))
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s", filer.Address)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/admin.templ`, Line: 361, Col: 111}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ templ ClusterFilers(data dash.ClusterFilersData) {
|
||||
for _, filer := range data.Filers {
|
||||
<tr>
|
||||
<td>
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("http://%s", filer.Address)) } target="_blank" class="text-decoration-none">
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("//%s", filer.Address)) } target="_blank" class="text-decoration-none">
|
||||
{ filer.Address }
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
|
||||
@@ -62,7 +62,7 @@ func ClusterFilers(data dash.ClusterFilersData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s", filer.Address)))
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s", filer.Address)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/cluster_filers.templ`, Line: 71, Col: 75}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ templ ClusterMasters(data dash.ClusterMastersData) {
|
||||
for _, master := range data.Masters {
|
||||
<tr>
|
||||
<td>
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("http://%s", master.Address)) } target="_blank" class="text-decoration-none">
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("//%s", master.Address)) } target="_blank" class="text-decoration-none">
|
||||
{ master.Address }
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -219,7 +219,7 @@ templ ClusterMasters(data dash.ClusterMastersData) {
|
||||
'<div class="col-md-6">' +
|
||||
'<h6 class="text-primary"><i class="fas fa-link me-1"></i>Quick Actions</h6>' +
|
||||
'<div class="d-grid gap-2">' +
|
||||
'<a href="http://' + address + '" target="_blank" class="btn btn-outline-primary">' +
|
||||
'<a href="//' + address + '" target="_blank" class="btn btn-outline-primary">' +
|
||||
'<i class="fas fa-external-link-alt me-1"></i>Open Master UI' +
|
||||
'</a>' +
|
||||
'</div>' +
|
||||
|
||||
@@ -90,7 +90,7 @@ func ClusterMasters(data dash.ClusterMastersData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s", master.Address)))
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s", master.Address)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/cluster_masters.templ`, Line: 113, Col: 76}
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func ClusterMasters(data dash.ClusterMastersData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</small></div></div></div><!-- JavaScript for cluster masters functionality --><script>\n\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t// Handle master action buttons\n\t\tdocument.addEventListener('click', function(e) {\n\t\t\tconst button = e.target.closest('[data-action]');\n\t\t\tif (!button) return;\n\t\t\t\n\t\t\tconst action = button.getAttribute('data-action');\n\t\t\tconst address = button.getAttribute('data-address');\n\t\t\t\n\t\t\tif (!address) return;\n\t\t\t\n\t\t\tswitch(action) {\n\t\t\t\tcase 'view-details':\n\t\t\t\t\tconst isLeader = button.getAttribute('data-leader') === 'true';\n\t\t\t\t\tconst suffrage = button.getAttribute('data-suffrage');\n\t\t\t\t\tshowMasterDetails(address, isLeader, suffrage);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t});\n\t\n\tfunction showMasterDetails(address, isLeader, suffrage) {\n\t\tconst modalHtml = '<div class=\"modal fade\" id=\"masterDetailsModal\" tabindex=\"-1\">' +\n\t\t\t'<div class=\"modal-dialog modal-lg\">' +\n\t\t\t'<div class=\"modal-content\">' +\n\t\t\t'<div class=\"modal-header\">' +\n\t\t\t'<h5 class=\"modal-title\"><i class=\"fas fa-crown me-2\"></i>Master Details: ' + address + '</h5>' +\n\t\t\t'<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-body\">' +\n\t\t\t'<div class=\"row\">' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-info-circle me-1\"></i>Basic Information</h6>' +\n\t\t\t'<table class=\"table table-sm\">' +\n\t\t\t'<tr><td><strong>Address:</strong></td><td>' + address + '</td></tr>' +\n\t\t\t'<tr><td><strong>Role:</strong></td><td>' + \n\t\t\t(isLeader ? '<span class=\"badge bg-warning text-dark\"><i class=\"fas fa-star me-1\"></i>Leader</span>' : \n\t\t\t'<span class=\"badge bg-secondary\">Follower</span>') + '</td></tr>' +\n\t\t\t'<tr><td><strong>Suffrage:</strong></td><td>' + (suffrage || 'N/A') + '</td></tr>' +\n\t\t\t'<tr><td><strong>Status:</strong></td><td><span class=\"badge bg-success\">Active</span></td></tr>' +\n\t\t\t'</table>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-link me-1\"></i>Quick Actions</h6>' +\n\t\t\t'<div class=\"d-grid gap-2\">' +\n\t\t\t'<a href=\"http://' + address + '\" target=\"_blank\" class=\"btn btn-outline-primary\">' +\n\t\t\t'<i class=\"fas fa-external-link-alt me-1\"></i>Open Master UI' +\n\t\t\t'</a>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-footer\">' +\n\t\t\t'<button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Close</button>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>';\n\t\t\n\t\t// Remove existing modal if present\n\t\tconst existingModal = document.getElementById('masterDetailsModal');\n\t\tif (existingModal) {\n\t\t\texistingModal.remove();\n\t\t}\n\t\t\n\t\t// Add modal to body and show\n\t\tdocument.body.insertAdjacentHTML('beforeend', modalHtml);\n\t\tconst modal = new bootstrap.Modal(document.getElementById('masterDetailsModal'));\n\t\tmodal.show();\n\t\t\n\t\t// Remove modal when hidden\n\t\tdocument.getElementById('masterDetailsModal').addEventListener('hidden.bs.modal', function() {\n\t\t\tthis.remove();\n\t\t});\n\t}\n\t\n\tfunction exportMasters() {\n\t\t// Simple CSV export of masters list\n\t\tconst rows = Array.from(document.querySelectorAll('#mastersTable tbody tr')).map(row => {\n\t\t\tconst cells = row.querySelectorAll('td');\n\t\t\tif (cells.length > 1) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: cells[0].textContent.trim(),\n\t\t\t\t\trole: cells[1].textContent.trim(),\n\t\t\t\t\tsuffrage: cells[2].textContent.trim()\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn null;\n\t\t}).filter(row => row !== null);\n\t\t\n\t\tconst csvContent = \"data:text/csv;charset=utf-8,\" + \n\t\t\t\"Address,Role,Suffrage\\n\" +\n\t\t\trows.map(r => '\"' + r.address + '\",\"' + r.role + '\",\"' + r.suffrage + '\"').join(\"\\n\");\n\t\t\n\t\tconst encodedUri = encodeURI(csvContent);\n\t\tconst link = document.createElement(\"a\");\n\t\tlink.setAttribute(\"href\", encodedUri);\n\t\tlink.setAttribute(\"download\", \"masters.csv\");\n\t\tdocument.body.appendChild(link);\n\t\tlink.click();\n\t\tdocument.body.removeChild(link);\n\t}\n\t</script>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</small></div></div></div><!-- JavaScript for cluster masters functionality --><script>\n\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t// Handle master action buttons\n\t\tdocument.addEventListener('click', function(e) {\n\t\t\tconst button = e.target.closest('[data-action]');\n\t\t\tif (!button) return;\n\t\t\t\n\t\t\tconst action = button.getAttribute('data-action');\n\t\t\tconst address = button.getAttribute('data-address');\n\t\t\t\n\t\t\tif (!address) return;\n\t\t\t\n\t\t\tswitch(action) {\n\t\t\t\tcase 'view-details':\n\t\t\t\t\tconst isLeader = button.getAttribute('data-leader') === 'true';\n\t\t\t\t\tconst suffrage = button.getAttribute('data-suffrage');\n\t\t\t\t\tshowMasterDetails(address, isLeader, suffrage);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\t});\n\t\n\tfunction showMasterDetails(address, isLeader, suffrage) {\n\t\tconst modalHtml = '<div class=\"modal fade\" id=\"masterDetailsModal\" tabindex=\"-1\">' +\n\t\t\t'<div class=\"modal-dialog modal-lg\">' +\n\t\t\t'<div class=\"modal-content\">' +\n\t\t\t'<div class=\"modal-header\">' +\n\t\t\t'<h5 class=\"modal-title\"><i class=\"fas fa-crown me-2\"></i>Master Details: ' + address + '</h5>' +\n\t\t\t'<button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\"></button>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-body\">' +\n\t\t\t'<div class=\"row\">' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-info-circle me-1\"></i>Basic Information</h6>' +\n\t\t\t'<table class=\"table table-sm\">' +\n\t\t\t'<tr><td><strong>Address:</strong></td><td>' + address + '</td></tr>' +\n\t\t\t'<tr><td><strong>Role:</strong></td><td>' + \n\t\t\t(isLeader ? '<span class=\"badge bg-warning text-dark\"><i class=\"fas fa-star me-1\"></i>Leader</span>' : \n\t\t\t'<span class=\"badge bg-secondary\">Follower</span>') + '</td></tr>' +\n\t\t\t'<tr><td><strong>Suffrage:</strong></td><td>' + (suffrage || 'N/A') + '</td></tr>' +\n\t\t\t'<tr><td><strong>Status:</strong></td><td><span class=\"badge bg-success\">Active</span></td></tr>' +\n\t\t\t'</table>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"col-md-6\">' +\n\t\t\t'<h6 class=\"text-primary\"><i class=\"fas fa-link me-1\"></i>Quick Actions</h6>' +\n\t\t\t'<div class=\"d-grid gap-2\">' +\n\t\t\t'<a href=\"//' + address + '\" target=\"_blank\" class=\"btn btn-outline-primary\">' +\n\t\t\t'<i class=\"fas fa-external-link-alt me-1\"></i>Open Master UI' +\n\t\t\t'</a>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'<div class=\"modal-footer\">' +\n\t\t\t'<button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Close</button>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>' +\n\t\t\t'</div>';\n\t\t\n\t\t// Remove existing modal if present\n\t\tconst existingModal = document.getElementById('masterDetailsModal');\n\t\tif (existingModal) {\n\t\t\texistingModal.remove();\n\t\t}\n\t\t\n\t\t// Add modal to body and show\n\t\tdocument.body.insertAdjacentHTML('beforeend', modalHtml);\n\t\tconst modal = new bootstrap.Modal(document.getElementById('masterDetailsModal'));\n\t\tmodal.show();\n\t\t\n\t\t// Remove modal when hidden\n\t\tdocument.getElementById('masterDetailsModal').addEventListener('hidden.bs.modal', function() {\n\t\t\tthis.remove();\n\t\t});\n\t}\n\t\n\tfunction exportMasters() {\n\t\t// Simple CSV export of masters list\n\t\tconst rows = Array.from(document.querySelectorAll('#mastersTable tbody tr')).map(row => {\n\t\t\tconst cells = row.querySelectorAll('td');\n\t\t\tif (cells.length > 1) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: cells[0].textContent.trim(),\n\t\t\t\t\trole: cells[1].textContent.trim(),\n\t\t\t\t\tsuffrage: cells[2].textContent.trim()\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn null;\n\t\t}).filter(row => row !== null);\n\t\t\n\t\tconst csvContent = \"data:text/csv;charset=utf-8,\" + \n\t\t\t\"Address,Role,Suffrage\\n\" +\n\t\t\trows.map(r => '\"' + r.address + '\",\"' + r.role + '\",\"' + r.suffrage + '\"').join(\"\\n\");\n\t\t\n\t\tconst encodedUri = encodeURI(csvContent);\n\t\tconst link = document.createElement(\"a\");\n\t\tlink.setAttribute(\"href\", encodedUri);\n\t\tlink.setAttribute(\"download\", \"masters.csv\");\n\t\tdocument.body.appendChild(link);\n\t\tlink.click();\n\t\tdocument.body.removeChild(link);\n\t}\n\t</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -114,12 +114,12 @@ templ ClusterVolumeServers(data dash.ClusterVolumeServersData) {
|
||||
<tr>
|
||||
<td>
|
||||
if host.PublicURL != "" {
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", host.PublicURL))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", host.PublicURL))} target="_blank" class="text-decoration-none">
|
||||
{host.Address}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
} else {
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", host.Address))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", host.Address))} target="_blank" class="text-decoration-none">
|
||||
{host.Address}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -334,7 +334,7 @@ templ ClusterVolumeServers(data dash.ClusterVolumeServersData) {
|
||||
'<div class="col-12">' +
|
||||
'<h6 class="text-primary"><i class="fas fa-link me-1"></i>Quick Actions</h6>' +
|
||||
'<div class="d-grid gap-2 d-md-flex">' +
|
||||
'<a href="http://' + (server.publicUrl || server.address) + '/ui/index.html" target="_blank" class="btn btn-outline-primary">' +
|
||||
'<a href="//' + (server.publicUrl || server.address) + '/ui/index.html" target="_blank" class="btn btn-outline-primary">' +
|
||||
'<i class="fas fa-external-link-alt me-1"></i>Open Volume Server UI' +
|
||||
'</a>' +
|
||||
'<a href="' + (window.__BASE_PATH__ || '') + '/storage/volumes?server=' + encodeURIComponent(server.address) + '" class="btn btn-outline-info">' +
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -320,7 +320,7 @@ templ ClusterVolumes(data dash.ClusterVolumesData) {
|
||||
</code>
|
||||
</td>
|
||||
<td>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", volume.Server))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", volume.Server))} target="_blank" class="text-decoration-none">
|
||||
{volume.Server}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
|
||||
@@ -496,7 +496,7 @@ func ClusterVolumes(data dash.ClusterVolumesData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 templ.SafeURL
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", volume.Server)))
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", volume.Server)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/cluster_volumes.templ`, Line: 323, Col: 121}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ templ EcVolumeDetails(data dash.EcVolumeDetailsData) {
|
||||
<span class="text-success">{bytesToHumanReadableUint64(shard.Size)}</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", shard.Server)) } target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-primary">
|
||||
<a href={ templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", shard.Server)) } target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-primary">
|
||||
<i class="fas fa-external-link-alt me-1"></i>Volume Server
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -525,7 +525,7 @@ func EcVolumeDetails(data dash.EcVolumeDetailsData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 templ.SafeURL
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", shard.Server)))
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", shard.Server)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/ec_volume_details.templ`, Line: 269, Col: 121}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ templ IcebergCatalog(data dash.IcebergCatalogData) {
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group me-2">
|
||||
<a id="iceberg-rest-link" href={ templ.SafeURL(fmt.Sprintf("http://localhost:%d/v1/config", data.IcebergPort)) } target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<a id="iceberg-rest-link" href={ templ.SafeURL(fmt.Sprintf("//localhost:%d/v1/config", data.IcebergPort)) } target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-external-link-alt me-1"></i>REST API
|
||||
</a>
|
||||
</div>
|
||||
@@ -35,16 +35,21 @@ templ IcebergCatalog(data dash.IcebergCatalogData) {
|
||||
<strong>Iceberg REST Catalog</strong>
|
||||
<p id="iceberg-rest-info" class="mb-0 mt-1" data-iceberg-port={ fmt.Sprintf("%d", data.IcebergPort) }>
|
||||
Connect your Iceberg clients (DuckDB, Spark, etc.) to:
|
||||
<code>http://<span id="iceberg-host">localhost</span>:{fmt.Sprintf("%d", data.IcebergPort)}/v1</code>
|
||||
<code><span id="iceberg-protocol">http</span>://<span id="iceberg-host">localhost</span>:{fmt.Sprintf("%d", data.IcebergPort)}/v1</code>
|
||||
</p>
|
||||
<script>
|
||||
const icebergInfo = document.getElementById('iceberg-rest-info');
|
||||
const icebergHost = window.location.hostname;
|
||||
const icebergProtocol = window.location.protocol.slice(0, -1);
|
||||
const icebergPort = icebergInfo ? icebergInfo.dataset.icebergPort : '';
|
||||
document.getElementById('iceberg-host').innerText = icebergHost;
|
||||
const icebergProtocolEl = document.getElementById('iceberg-protocol');
|
||||
if (icebergProtocolEl) {
|
||||
icebergProtocolEl.innerText = icebergProtocol;
|
||||
}
|
||||
const restLink = document.getElementById('iceberg-rest-link');
|
||||
if (restLink && icebergPort) {
|
||||
restLink.href = `http://${icebergHost}:${icebergPort}/v1/config`;
|
||||
restLink.href = `//${icebergHost}:${icebergPort}/v1/config`;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
||||
@@ -55,7 +55,7 @@ func IcebergCatalog(data dash.IcebergCatalogData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://localhost:%d/v1/config", data.IcebergPort)))
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//localhost:%d/v1/config", data.IcebergPort)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/iceberg_catalog.templ`, Line: 23, Col: 114}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func IcebergCatalog(data dash.IcebergCatalogData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">Connect your Iceberg clients (DuckDB, Spark, etc.) to: <code>http://<span id=\"iceberg-host\">localhost</span>:")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\">Connect your Iceberg clients (DuckDB, Spark, etc.) to: <code><span id=\"iceberg-protocol\">http</span>://<span id=\"iceberg-host\">localhost</span>:")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -89,7 +89,7 @@ func IcebergCatalog(data dash.IcebergCatalogData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "/v1</code></p><script>\n\t\t\t\t\t\tconst icebergInfo = document.getElementById('iceberg-rest-info');\n\t\t\t\t\t\tconst icebergHost = window.location.hostname;\n\t\t\t\t\t\tconst icebergPort = icebergInfo ? icebergInfo.dataset.icebergPort : '';\n\t\t\t\t\t\tdocument.getElementById('iceberg-host').innerText = icebergHost;\n\t\t\t\t\t\tconst restLink = document.getElementById('iceberg-rest-link');\n\t\t\t\t\t\tif (restLink && icebergPort) {\n\t\t\t\t\t\t\trestLink.href = `http://${icebergHost}:${icebergPort}/v1/config`;\n\t\t\t\t\t\t}\n\t\t\t\t\t</script></div></div></div><!-- Stats Cards --><div class=\"row mb-4\"><div class=\"col-xl-4 col-md-6 mb-4\"><div class=\"card border-left-primary shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-primary text-uppercase mb-1\">Catalogs (Table Buckets)</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "/v1</code></p><script>\n\t\t\t\t\t\tconst icebergInfo = document.getElementById('iceberg-rest-info');\n\t\t\t\t\t\tconst icebergHost = window.location.hostname;\n\t\t\t\t\t\tconst icebergProtocol = window.location.protocol.slice(0, -1);\n\t\t\t\t\t\tconst icebergPort = icebergInfo ? icebergInfo.dataset.icebergPort : '';\n\t\t\t\t\t\tdocument.getElementById('iceberg-host').innerText = icebergHost;\n\t\t\t\t\t\tconst icebergProtocolEl = document.getElementById('iceberg-protocol');\n\t\t\t\t\t\tif (icebergProtocolEl) {\n\t\t\t\t\t\t\ticebergProtocolEl.innerText = icebergProtocol;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst restLink = document.getElementById('iceberg-rest-link');\n\t\t\t\t\t\tif (restLink && icebergPort) {\n\t\t\t\t\t\t\trestLink.href = `//${icebergHost}:${icebergPort}/v1/config`;\n\t\t\t\t\t\t}\n\t\t\t\t\t</script></div></div></div><!-- Stats Cards --><div class=\"row mb-4\"><div class=\"col-xl-4 col-md-6 mb-4\"><div class=\"card border-left-primary shadow h-100 py-2\"><div class=\"card-body\"><div class=\"row no-gutters align-items-center\"><div class=\"col mr-2\"><div class=\"text-xs font-weight-bold text-primary text-uppercase mb-1\">Catalogs (Table Buckets)</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ templ S3TablesBuckets(data dash.S3TablesBucketsData) {
|
||||
</div>
|
||||
if data.IcebergPort > 0 {
|
||||
<div class="btn-group me-2">
|
||||
<a id="s3tables-iceberg-rest-link" href={ templ.SafeURL(fmt.Sprintf("http://localhost:%d/v1/config", data.IcebergPort)) } target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<a id="s3tables-iceberg-rest-link" href={ templ.SafeURL(fmt.Sprintf("//localhost:%d/v1/config", data.IcebergPort)) } target="_blank" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-snowflake me-1"></i>Iceberg REST API
|
||||
</a>
|
||||
</div>
|
||||
@@ -37,16 +37,21 @@ templ S3TablesBuckets(data dash.S3TablesBucketsData) {
|
||||
<strong>Iceberg REST Catalog</strong>
|
||||
<p id="s3tables-iceberg-info" class="mb-0 mt-1" data-iceberg-port={ fmt.Sprintf("%d", data.IcebergPort) }>
|
||||
Each table bucket is an Iceberg catalog. Connect clients to:
|
||||
<code>http://<span id="s3tables-iceberg-host">localhost</span>:{ fmt.Sprintf("%d", data.IcebergPort) }/v1</code>
|
||||
<code><span id="s3tables-iceberg-protocol">http</span>://<span id="s3tables-iceberg-host">localhost</span>:{ fmt.Sprintf("%d", data.IcebergPort) }/v1</code>
|
||||
</p>
|
||||
<script>
|
||||
const s3tablesInfo = document.getElementById('s3tables-iceberg-info');
|
||||
const s3tablesIcebergHost = window.location.hostname;
|
||||
const s3tablesIcebergProtocol = window.location.protocol.slice(0, -1);
|
||||
const s3tablesIcebergPort = s3tablesInfo ? s3tablesInfo.dataset.icebergPort : '';
|
||||
document.getElementById('s3tables-iceberg-host').innerText = s3tablesIcebergHost;
|
||||
const s3tablesIcebergProtocolEl = document.getElementById('s3tables-iceberg-protocol');
|
||||
if (s3tablesIcebergProtocolEl) {
|
||||
s3tablesIcebergProtocolEl.innerText = s3tablesIcebergProtocol;
|
||||
}
|
||||
const s3tablesRestLink = document.getElementById('s3tables-iceberg-rest-link');
|
||||
if (s3tablesRestLink && s3tablesIcebergPort) {
|
||||
s3tablesRestLink.href = `http://${s3tablesIcebergHost}:${s3tablesIcebergPort}/v1/config`;
|
||||
s3tablesRestLink.href = `//${s3tablesIcebergHost}:${s3tablesIcebergPort}/v1/config`;
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ func S3TablesBuckets(data dash.S3TablesBucketsData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 templ.SafeURL
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://localhost:%d/v1/config", data.IcebergPort)))
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//localhost:%d/v1/config", data.IcebergPort)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/s3tables_buckets.templ`, Line: 24, Col: 124}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func S3TablesBuckets(data dash.S3TablesBucketsData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">Each table bucket is an Iceberg catalog. Connect clients to: <code>http://<span id=\"s3tables-iceberg-host\">localhost</span>:")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">Each table bucket is an Iceberg catalog. Connect clients to: <code><span id=\"s3tables-iceberg-protocol\">http</span>://<span id=\"s3tables-iceberg-host\">localhost</span>:")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func S3TablesBuckets(data dash.S3TablesBucketsData) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "/v1</code></p><script>\n\t\t\t\t\t\t\tconst s3tablesInfo = document.getElementById('s3tables-iceberg-info');\n\t\t\t\t\t\t\tconst s3tablesIcebergHost = window.location.hostname;\n\t\t\t\t\t\t\tconst s3tablesIcebergPort = s3tablesInfo ? s3tablesInfo.dataset.icebergPort : '';\n\t\t\t\t\t\t\tdocument.getElementById('s3tables-iceberg-host').innerText = s3tablesIcebergHost;\n\t\t\t\t\t\t\tconst s3tablesRestLink = document.getElementById('s3tables-iceberg-rest-link');\n\t\t\t\t\t\t\tif (s3tablesRestLink && s3tablesIcebergPort) {\n\t\t\t\t\t\t\t\ts3tablesRestLink.href = `http://${s3tablesIcebergHost}:${s3tablesIcebergPort}/v1/config`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</script></div></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "/v1</code></p><script>\n\t\t\t\t\t\t\tconst s3tablesInfo = document.getElementById('s3tables-iceberg-info');\n\t\t\t\t\t\t\tconst s3tablesIcebergHost = window.location.hostname;\n\t\t\t\t\t\t\tconst s3tablesIcebergProtocol = window.location.protocol.slice(0, -1);\n\t\t\t\t\t\t\tconst s3tablesIcebergPort = s3tablesInfo ? s3tablesInfo.dataset.icebergPort : '';\n\t\t\t\t\t\t\tdocument.getElementById('s3tables-iceberg-host').innerText = s3tablesIcebergHost;\n\t\t\t\t\t\t\tconst s3tablesIcebergProtocolEl = document.getElementById('s3tables-iceberg-protocol');\n\t\t\t\t\t\t\tif (s3tablesIcebergProtocolEl) {\n\t\t\t\t\t\t\t\ts3tablesIcebergProtocolEl.innerText = s3tablesIcebergProtocol;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst s3tablesRestLink = document.getElementById('s3tables-iceberg-rest-link');\n\t\t\t\t\t\t\tif (s3tablesRestLink && s3tablesIcebergPort) {\n\t\t\t\t\t\t\t\ts3tablesRestLink.href = `//${s3tablesIcebergHost}:${s3tablesIcebergPort}/v1/config`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</script></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ templ VolumeDetails(data dash.VolumeDetailsData) {
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>Server:</strong></label>
|
||||
<div>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", data.Volume.Server))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", data.Volume.Server))} target="_blank" class="text-decoration-none">
|
||||
{data.Volume.Server}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -298,7 +298,7 @@ templ VolumeDetails(data dash.VolumeDetailsData) {
|
||||
<tr class="table-primary">
|
||||
<td>
|
||||
<strong>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", data.Volume.Server))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", data.Volume.Server))} target="_blank" class="text-decoration-none">
|
||||
{data.Volume.Server}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
@@ -318,7 +318,7 @@ templ VolumeDetails(data dash.VolumeDetailsData) {
|
||||
for _, replica := range data.Replicas {
|
||||
<tr>
|
||||
<td>
|
||||
<a href={templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", replica.Server))} target="_blank" class="text-decoration-none">
|
||||
<a href={templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", replica.Server))} target="_blank" class="text-decoration-none">
|
||||
{replica.Server}
|
||||
<i class="fas fa-external-link-alt ms-1 text-muted"></i>
|
||||
</a>
|
||||
|
||||
@@ -92,7 +92,7 @@ func VolumeDetails(data dash.VolumeDetailsData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", data.Volume.Server)))
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", data.Volume.Server)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/volume_details.templ`, Line: 54, Col: 118}
|
||||
}
|
||||
@@ -500,7 +500,7 @@ func VolumeDetails(data dash.VolumeDetailsData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 templ.SafeURL
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", data.Volume.Server)))
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", data.Volume.Server)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/volume_details.templ`, Line: 301, Col: 130}
|
||||
}
|
||||
@@ -583,7 +583,7 @@ func VolumeDetails(data dash.VolumeDetailsData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 templ.SafeURL
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("http://%s/ui/index.html", replica.Server)))
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("//%s/ui/index.html", replica.Server)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `weed/admin/view/app/volume_details.templ`, Line: 321, Col: 126}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user