You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
6.7 KiB
215 lines
6.7 KiB
package app
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3tables"
|
|
)
|
|
|
|
templ IcebergCatalog(data dash.IcebergCatalogData) {
|
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
|
<h1 class="h2">
|
|
<i class="fas fa-snowflake me-2"></i>Iceberg Catalog
|
|
</h1>
|
|
<div class="btn-toolbar mb-2 mb-md-0">
|
|
<div class="btn-group me-2">
|
|
<a href={ templ.SafeURL("/v1/config") } target="_blank" class="btn btn-sm btn-outline-secondary">
|
|
<i class="fas fa-external-link-alt me-1"></i>REST API
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="iceberg-catalog-content">
|
|
<!-- Info Alert about Iceberg REST -->
|
|
<div class="alert alert-info mb-4">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-info-circle fa-2x me-3"></i>
|
|
<div>
|
|
<strong>Iceberg REST Catalog</strong>
|
|
<p class="mb-0 mt-1">
|
|
Connect your Iceberg clients (DuckDB, Spark, etc.) to:
|
|
<code>http://<span id="iceberg-host">localhost</span>:{fmt.Sprintf("%d", data.IcebergPort)}/v1</code>
|
|
</p>
|
|
<script>
|
|
document.getElementById('iceberg-host').innerText = window.location.hostname;
|
|
</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">
|
|
{ fmt.Sprintf("%d", data.TotalCatalogs) }
|
|
</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-database fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4 col-md-6 mb-4">
|
|
<div class="card border-left-success 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-success text-uppercase mb-1">
|
|
REST Port
|
|
</div>
|
|
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
|
{ fmt.Sprintf("%d", data.IcebergPort) }
|
|
</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-plug fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-4 col-md-6 mb-4">
|
|
<div class="card border-left-info 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-info text-uppercase mb-1">
|
|
Last Updated
|
|
</div>
|
|
<div class="h6 mb-0 font-weight-bold text-gray-800">
|
|
{ data.LastUpdated.Format("15:04") }
|
|
</div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<i class="fas fa-clock fa-2x text-gray-300"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Catalog List -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card shadow mb-4">
|
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
|
<h6 class="m-0 font-weight-bold text-primary">
|
|
<i class="fas fa-snowflake me-2"></i>Available Catalogs
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted mb-3">
|
|
Each S3 Table Bucket acts as an Iceberg catalog. Use the bucket name as the catalog prefix in your REST API calls.
|
|
</p>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover" width="100%" cellspacing="0" id="icebergCatalogsTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Catalog Name</th>
|
|
<th>Owner</th>
|
|
<th>REST Endpoint</th>
|
|
<th>Created</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
for _, catalog := range data.Catalogs {
|
|
<tr>
|
|
<td>
|
|
<i class="fas fa-snowflake text-info me-2"></i>
|
|
<strong>{ catalog.Name }</strong>
|
|
</td>
|
|
<td>{ catalog.OwnerAccountID }</td>
|
|
<td>
|
|
<code class="small">/v1/{ catalog.Name }/namespaces</code>
|
|
</td>
|
|
<td>{ catalog.CreatedAt.Format("2006-01-02 15:04") }</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm" role="group">
|
|
{{ bucketName, parseErr := s3tables.ParseBucketNameFromARN(catalog.ARN) }}
|
|
if parseErr == nil {
|
|
<a class="btn btn-outline-primary btn-sm" href={ templ.SafeURL(fmt.Sprintf("/object-store/iceberg/%s/namespaces", bucketName)) } title="Browse Namespaces">
|
|
<i class="fas fa-folder-open"></i>
|
|
</a>
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
}
|
|
if len(data.Catalogs) == 0 {
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted py-4">
|
|
<i class="fas fa-snowflake fa-3x mb-3 text-muted"></i>
|
|
<div>
|
|
<h5>No catalogs available</h5>
|
|
<p>Create an S3 Table Bucket first to use as an Iceberg catalog.</p>
|
|
<a href="/object-store/s3tables/buckets" class="btn btn-primary">
|
|
<i class="fas fa-plus me-1"></i>Create Table Bucket
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Example Usage Card -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card shadow mb-4">
|
|
<div class="card-header py-3">
|
|
<h6 class="m-0 font-weight-bold text-primary">
|
|
<i class="fas fa-code me-2"></i>Example Usage
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<h6>DuckDB</h6>
|
|
<pre class="bg-light p-3 border rounded"><code>{ `-- Install and load Iceberg extension
|
|
INSTALL iceberg;
|
|
LOAD iceberg;
|
|
|
|
-- Create a catalog connection
|
|
CREATE SECRET (
|
|
TYPE ICEBERG,
|
|
ENDPOINT 'http://localhost:` + fmt.Sprintf("%d", data.IcebergPort) + `',
|
|
SCOPE 's3://my-table-bucket/'
|
|
);
|
|
|
|
-- Query tables
|
|
SELECT * FROM iceberg_scan('s3://my-table-bucket/my-namespace/my-table');` }</code></pre>
|
|
|
|
<h6 class="mt-4">Python (PyIceberg)</h6>
|
|
<pre class="bg-light p-3 border rounded"><code>{ `from pyiceberg.catalog import load_catalog
|
|
|
|
catalog = load_catalog(
|
|
name="seaweedfs",
|
|
**{
|
|
"type": "rest",
|
|
"uri": "http://localhost:` + fmt.Sprintf("%d", data.IcebergPort) + `",
|
|
}
|
|
)
|
|
|
|
# List namespaces
|
|
namespaces = catalog.list_namespaces()` }</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|