* Add shared s3tables manager * Add s3tables shell commands * Add s3tables admin API * Add s3tables admin UI * Fix admin s3tables namespace create * Rename table buckets menu * Centralize s3tables tag validation * Reuse s3tables manager in admin * Extract s3tables list limit * Add s3tables bucket ARN helper * Remove write middleware from s3tables APIs * Fix bucket link and policy hint * Fix table tag parsing and nav link * Disable namespace table link on invalid ARN * Improve s3tables error decode * Return flag parse errors for s3tables tag * Accept query params for namespace create * Bind namespace create form data * Read s3tables JS data from DOM * s3tables: allow empty region ARN * shell: pass s3tables account id * shell: require account for table buckets * shell: use bucket name for namespaces * shell: use bucket name for tables * shell: use bucket name for tags * admin: add table buckets links in file browser * s3api: reuse s3tables tag validation * admin: harden s3tables UI handlers * fix admin list table buckets * allow admin s3tables access * validate s3tables bucket tags * log s3tables bucket metadata errors * rollback table bucket on owner failure * show s3tables bucket owner * add s3tables iam conditions * Add s3tables user permissions UI * Authorize s3tables using identity actions * Add s3tables permissions to user modal * Disambiguate bucket scope in user permissions * Block table bucket names that match S3 buckets * Pretty-print IAM identity JSON * Include tags in s3tables permission context * admin: refactor S3 Tables inline JavaScript into a separate file * s3tables: extend IAM policy condition operators support * shell: use LookupEntry wrapper for s3tables bucket conflict check * admin: handle buildBucketPermissions validation in create/update flows
223 lines
28 KiB
Go
223 lines
28 KiB
Go
// Code generated by templ - DO NOT EDIT.
|
|
|
|
// templ: version: v0.3.960
|
|
package app
|
|
|
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|
|
|
import "github.com/a-h/templ"
|
|
import templruntime "github.com/a-h/templ/runtime"
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/admin/dash"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3tables"
|
|
)
|
|
|
|
func S3TablesBuckets(data dash.S3TablesBucketsData) templ.Component {
|
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
|
return templ_7745c5c3_CtxErr
|
|
}
|
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
|
if !templ_7745c5c3_IsBuffer {
|
|
defer func() {
|
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
|
if templ_7745c5c3_Err == nil {
|
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
|
}
|
|
}()
|
|
}
|
|
ctx = templ.InitializeContext(ctx)
|
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
|
if templ_7745c5c3_Var1 == nil {
|
|
templ_7745c5c3_Var1 = templ.NopComponent
|
|
}
|
|
ctx = templ.ClearChildren(ctx)
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<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-table me-2\"></i>S3 Tables Buckets</h1><div class=\"btn-toolbar mb-2 mb-md-0\"><div class=\"btn-group me-2\"><button type=\"button\" class=\"btn btn-sm btn-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#createS3TablesBucketModal\"><i class=\"fas fa-plus me-1\"></i>Create Bucket</button></div></div></div><div id=\"s3tables-buckets-content\"><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\">Total Buckets</div><div class=\"h5 mb-0 font-weight-bold text-gray-800\">")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var2 string
|
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.TotalBuckets))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 34, Col: 47}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "</div></div><div class=\"col-auto\"><i class=\"fas fa-table 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\">")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var3 string
|
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastUpdated.Format("15:04"))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 53, Col: 43}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</div></div><div class=\"col-auto\"><i class=\"fas fa-clock fa-2x text-gray-300\"></i></div></div></div></div></div></div><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-table me-2\"></i>Table Buckets</h6></div><div class=\"card-body\"><div class=\"table-responsive\"><table class=\"table table-hover\" width=\"100%\" cellspacing=\"0\" id=\"s3tablesBucketsTable\"><thead><tr><th>Name</th><th>Owner</th><th>ARN</th><th>Created</th><th>Actions</th></tr></thead> <tbody>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
for _, bucket := range data.Buckets {
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<tr><td>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var4 string
|
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.Name)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 87, Col: 28}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</td><td>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var5 string
|
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.OwnerAccountID)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 88, Col: 38}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</td><td class=\"text-muted small\">")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var6 string
|
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.ARN)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 89, Col: 52}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</td><td>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var7 string
|
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.CreatedAt.Format("2006-01-02 15:04"))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 90, Col: 60}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</td><td><div class=\"btn-group btn-group-sm\" role=\"group\">")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
bucketName, parseErr := s3tables.ParseBucketNameFromARN(bucket.ARN)
|
|
if parseErr == nil {
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<a class=\"btn btn-outline-primary btn-sm\" href=\"")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var8 templ.SafeURL
|
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/object-store/s3tables/buckets/%s/namespaces", bucketName)))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 95, Col: 149}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\"><i class=\"fas fa-folder-open\"></i></a> ")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
} else {
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<button type=\"button\" class=\"btn btn-outline-primary btn-sm\" disabled title=\"Invalid bucket ARN\"><i class=\"fas fa-folder-open\"></i></button> ")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<button type=\"button\" class=\"btn btn-outline-success btn-sm s3tables-tags-btn\" data-resource-arn=\"")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var9 string
|
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.ARN)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 103, Col: 122}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" title=\"Tags\"><i class=\"fas fa-tags\"></i></button> <button type=\"button\" class=\"btn btn-outline-info btn-sm s3tables-bucket-policy-btn\" data-bucket-arn=\"")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var10 string
|
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.ARN)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 106, Col: 126}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" title=\"Bucket Policy\"><i class=\"fas fa-shield-alt\"></i></button> <button type=\"button\" class=\"btn btn-outline-danger btn-sm s3tables-delete-bucket-btn\" data-bucket-arn=\"")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var11 string
|
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.ARN)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 109, Col: 128}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" data-bucket-name=\"")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
var templ_7745c5c3_Var12 string
|
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.Name)
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3tables_buckets.templ`, Line: 109, Col: 161}
|
|
}
|
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" title=\"Delete\"><i class=\"fas fa-trash\"></i></button></div></td></tr>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
}
|
|
if len(data.Buckets) == 0 {
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<tr><td colspan=\"5\" class=\"text-center text-muted py-4\"><i class=\"fas fa-table fa-3x mb-3 text-muted\"></i><div><h5>No table buckets found</h5><p>Create your first S3 Tables bucket to get started.</p><button type=\"button\" class=\"btn btn-primary\" data-bs-toggle=\"modal\" data-bs-target=\"#createS3TablesBucketModal\"><i class=\"fas fa-plus me-1\"></i>Create Bucket</button></div></td></tr>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
}
|
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</tbody></table></div></div></div></div></div></div><div class=\"modal fade\" id=\"createS3TablesBucketModal\" tabindex=\"-1\" aria-labelledby=\"createS3TablesBucketModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"createS3TablesBucketModalLabel\"><i class=\"fas fa-plus me-2\"></i>Create Table Bucket</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><form id=\"createS3TablesBucketForm\"><div class=\"modal-body\"><div class=\"mb-3\"><label for=\"s3tablesBucketName\" class=\"form-label\">Bucket Name</label> <input type=\"text\" class=\"form-control\" id=\"s3tablesBucketName\" name=\"name\" placeholder=\"table-bucket-name\" required></div><div class=\"mb-3\"><label for=\"s3tablesBucketOwner\" class=\"form-label\">Owner (Optional)</label> <select class=\"form-select\" id=\"s3tablesBucketOwner\" name=\"owner\"><option value=\"\">No owner (admin-only access)</option></select><div class=\"form-text\">The S3 identity that owns this table bucket. Non-admin users can only access table buckets they own.</div></div><div class=\"mb-3\"><label for=\"s3tablesBucketTags\" class=\"form-label\">Tags</label> <input type=\"text\" class=\"form-control\" id=\"s3tablesBucketTags\" name=\"tags\" placeholder=\"key1=value1,key2=value2\"><div class=\"form-text\">Optional tags in key=value format.</div></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"submit\" class=\"btn btn-primary\"><i class=\"fas fa-plus me-1\"></i>Create</button></div></form></div></div></div><div class=\"modal fade\" id=\"deleteS3TablesBucketModal\" tabindex=\"-1\" aria-labelledby=\"deleteS3TablesBucketModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"deleteS3TablesBucketModalLabel\"><i class=\"fas fa-exclamation-triangle me-2 text-warning\"></i>Delete Table Bucket</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><div class=\"modal-body\"><p>Are you sure you want to delete the table bucket <strong id=\"deleteS3TablesBucketName\"></strong>?</p></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button> <button type=\"button\" class=\"btn btn-danger\" onclick=\"deleteS3TablesBucket()\"><i class=\"fas fa-trash me-1\"></i>Delete</button></div></div></div></div><div class=\"modal fade\" id=\"s3tablesBucketPolicyModal\" tabindex=\"-1\" aria-labelledby=\"s3tablesBucketPolicyModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog modal-lg\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"s3tablesBucketPolicyModalLabel\"><i class=\"fas fa-shield-alt me-2\"></i>Table Bucket Policy</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><form id=\"s3tablesBucketPolicyForm\"><div class=\"modal-body\"><input type=\"hidden\" id=\"s3tablesBucketPolicyArn\" name=\"bucket_arn\"><div class=\"mb-3\"><label for=\"s3tablesBucketPolicyText\" class=\"form-label\">Policy JSON</label> <textarea class=\"form-control\" id=\"s3tablesBucketPolicyText\" name=\"policy\" rows=\"12\" placeholder=\"{ }\"></textarea></div><div class=\"form-text\">Provide a policy JSON; use Delete Policy to remove the policy.</div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Close</button> <button type=\"button\" class=\"btn btn-outline-danger\" onclick=\"deleteS3TablesBucketPolicy()\"><i class=\"fas fa-trash me-1\"></i>Delete Policy</button> <button type=\"submit\" class=\"btn btn-primary\"><i class=\"fas fa-save me-1\"></i>Save Policy</button></div></form></div></div></div><div class=\"modal fade\" id=\"s3tablesTagsModal\" tabindex=\"-1\" aria-labelledby=\"s3tablesTagsModalLabel\" aria-hidden=\"true\"><div class=\"modal-dialog modal-lg\"><div class=\"modal-content\"><div class=\"modal-header\"><h5 class=\"modal-title\" id=\"s3tablesTagsModalLabel\"><i class=\"fas fa-tags me-2\"></i>Resource Tags</h5><button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button></div><form id=\"s3tablesTagsForm\"><div class=\"modal-body\"><input type=\"hidden\" id=\"s3tablesTagsResourceArn\" name=\"resource_arn\"><div class=\"mb-3\"><label class=\"form-label\">Existing Tags</label><pre class=\"bg-light p-3 border rounded\" id=\"s3tablesTagsList\">Loading...</pre></div><div class=\"mb-3\"><label for=\"s3tablesTagsInput\" class=\"form-label\">Add or Update Tags</label> <input type=\"text\" class=\"form-control\" id=\"s3tablesTagsInput\" placeholder=\"key1=value1,key2=value2\"></div><div class=\"mb-3\"><label for=\"s3tablesTagsDeleteInput\" class=\"form-label\">Remove Tag Keys</label> <input type=\"text\" class=\"form-control\" id=\"s3tablesTagsDeleteInput\" placeholder=\"key1,key2\"></div></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Close</button> <button type=\"button\" class=\"btn btn-outline-danger\" onclick=\"deleteS3TablesTags()\"><i class=\"fas fa-trash me-1\"></i>Remove Tags</button> <button type=\"submit\" class=\"btn btn-primary\"><i class=\"fas fa-save me-1\"></i>Update Tags</button></div></form></div></div></div><script>\n\t\tlet s3tablesBucketDeleteModal = null;\n\t\tlet s3tablesBucketPolicyModal = null;\n\t\tlet s3tablesTagsModal = null;\n\n\t\tdocument.addEventListener('DOMContentLoaded', function() {\n\t\t\ts3tablesBucketDeleteModal = new bootstrap.Modal(document.getElementById('deleteS3TablesBucketModal'));\n\t\t\ts3tablesBucketPolicyModal = new bootstrap.Modal(document.getElementById('s3tablesBucketPolicyModal'));\n\t\t\ts3tablesTagsModal = new bootstrap.Modal(document.getElementById('s3tablesTagsModal'));\n\t\t\tconst ownerSelect = document.getElementById('s3tablesBucketOwner');\n\n\t\t\tdocument.getElementById('createS3TablesBucketModal').addEventListener('show.bs.modal', async function() {\n\t\t\t\tif (ownerSelect.options.length <= 1) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst response = await fetch('/api/users');\n\t\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\t\tconst users = data.users || [];\n\t\t\t\t\t\tusers.forEach(user => {\n\t\t\t\t\t\t\tconst option = document.createElement('option');\n\t\t\t\t\t\t\toption.value = user.username;\n\t\t\t\t\t\t\toption.textContent = user.username;\n\t\t\t\t\t\t\townerSelect.appendChild(option);\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error('Error fetching users for owner dropdown:', error);\n\t\t\t\t\t\townerSelect.innerHTML = '<option value=\"\">No owner (admin-only access)</option>';\n\t\t\t\t\t\townerSelect.selectedIndex = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tdocument.querySelectorAll('.s3tables-delete-bucket-btn').forEach(button => {\n\t\t\t\tbutton.addEventListener('click', function() {\n\t\t\t\t\tdocument.getElementById('deleteS3TablesBucketName').textContent = this.dataset.bucketName || '';\n\t\t\t\t\tdocument.getElementById('deleteS3TablesBucketModal').dataset.bucketArn = this.dataset.bucketArn || '';\n\t\t\t\t\ts3tablesBucketDeleteModal.show();\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdocument.querySelectorAll('.s3tables-bucket-policy-btn').forEach(button => {\n\t\t\t\tbutton.addEventListener('click', function() {\n\t\t\t\t\tconst bucketArn = this.dataset.bucketArn || '';\n\t\t\t\t\tdocument.getElementById('s3tablesBucketPolicyArn').value = bucketArn;\n\t\t\t\t\tloadS3TablesBucketPolicy(bucketArn);\n\t\t\t\t\ts3tablesBucketPolicyModal.show();\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdocument.querySelectorAll('.s3tables-tags-btn').forEach(button => {\n\t\t\t\tbutton.addEventListener('click', function() {\n\t\t\t\t\tconst resourceArn = this.dataset.resourceArn || '';\n\t\t\t\t\topenS3TablesTags(resourceArn);\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tdocument.getElementById('createS3TablesBucketForm').addEventListener('submit', async function(e) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst name = document.getElementById('s3tablesBucketName').value.trim();\n\t\t\t\tconst owner = ownerSelect.value;\n\t\t\t\tconst tagsInput = document.getElementById('s3tablesBucketTags').value.trim();\n\t\t\t\tconst tags = parseTagsInput(tagsInput);\n\t\t\t\tif (tags === null) return;\n\t\t\t\tconst payload = { name: name, tags: tags, owner: owner };\n\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetch('/api/s3tables/buckets', {\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t\t\t});\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\talert(data.error || 'Failed to create bucket');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\talert('Bucket created successfully');\n\t\t\t\t\tlocation.reload();\n\t\t\t\t} catch (error) {\n\t\t\t\t\talert('Failed to create bucket: ' + error.message);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tdocument.getElementById('s3tablesBucketPolicyForm').addEventListener('submit', async function(e) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst bucketArn = document.getElementById('s3tablesBucketPolicyArn').value;\n\t\t\t\tconst policy = document.getElementById('s3tablesBucketPolicyText').value.trim();\n\t\t\t\tif (!policy) {\n\t\t\t\t\talert('Policy JSON is required');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetch('/api/s3tables/bucket-policy', {\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t\t\tbody: JSON.stringify({ bucket_arn: bucketArn, policy: policy })\n\t\t\t\t\t});\n\t\t\t\t\tconst data = await response.json();\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\talert(data.error || 'Failed to update policy');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\talert('Policy updated');\n\t\t\t\t\ts3tablesBucketPolicyModal.hide();\n\t\t\t\t} catch (error) {\n\t\t\t\t\talert('Failed to update policy: ' + error.message);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tdocument.getElementById('s3tablesTagsForm').addEventListener('submit', async function(e) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst resourceArn = document.getElementById('s3tablesTagsResourceArn').value;\n\t\t\t\tconst tags = parseTagsInput(document.getElementById('s3tablesTagsInput').value.trim());\n\t\t\t\tif (tags === null || Object.keys(tags).length === 0) {\n\t\t\t\t\talert('Please provide tags to update');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait updateS3TablesTags(resourceArn, tags);\n\t\t\t});\n\t\t});\n\n\t\tasync function deleteS3TablesBucket() {\n\t\t\tconst bucketArn = document.getElementById('deleteS3TablesBucketModal').dataset.bucketArn;\n\t\t\tif (!bucketArn) return;\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`/api/s3tables/buckets?bucket=${encodeURIComponent(bucketArn)}`, { method: 'DELETE' });\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\talert(data.error || 'Failed to delete bucket');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\talert('Bucket deleted');\n\t\t\t\tlocation.reload();\n\t\t\t} catch (error) {\n\t\t\t\talert('Failed to delete bucket: ' + error.message);\n\t\t\t}\n\t\t}\n\n\t\tasync function loadS3TablesBucketPolicy(bucketArn) {\n\t\t\tdocument.getElementById('s3tablesBucketPolicyText').value = '';\n\t\t\tif (!bucketArn) return;\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`/api/s3tables/bucket-policy?bucket=${encodeURIComponent(bucketArn)}`);\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (response.ok && data.policy) {\n\t\t\t\t\tdocument.getElementById('s3tablesBucketPolicyText').value = data.policy;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Failed to load bucket policy', error);\n\t\t\t}\n\t\t}\n\n\t\tasync function deleteS3TablesBucketPolicy() {\n\t\t\tconst bucketArn = document.getElementById('s3tablesBucketPolicyArn').value;\n\t\t\tif (!bucketArn) return;\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`/api/s3tables/bucket-policy?bucket=${encodeURIComponent(bucketArn)}`, { method: 'DELETE' });\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\talert(data.error || 'Failed to delete policy');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\talert('Policy deleted');\n\t\t\t\tdocument.getElementById('s3tablesBucketPolicyText').value = '';\n\t\t\t} catch (error) {\n\t\t\t\talert('Failed to delete policy: ' + error.message);\n\t\t\t}\n\t\t}\n\n\t\tfunction parseTagsInput(input) {\n\t\t\tif (!input) return {};\n\t\t\tconst tags = {};\n\t\t\tconst maxTags = 10;\n\t\t\tconst maxKeyLength = 128;\n\t\t\tconst maxValueLength = 256;\n\t\t\tconst parts = input.split(',');\n\t\t\tfor (const part of parts) {\n\t\t\t\tconst trimmedPart = part.trim();\n\t\t\t\tif (!trimmedPart) continue;\n\t\t\t\tconst idx = trimmedPart.indexOf('=');\n\t\t\t\tif (idx <= 0) {\n\t\t\t\t\talert('Invalid tag format. Use key=value, and key cannot be empty.');\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst key = trimmedPart.slice(0, idx).trim();\n\t\t\t\tconst value = trimmedPart.slice(idx + 1).trim();\n\t\t\t\tif (!key) {\n\t\t\t\t\talert('Invalid tag format. Use key=value, and key cannot be empty.');\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tif (key.length > maxKeyLength) {\n\t\t\t\t\talert(`Tag key length must be <= ${maxKeyLength}`);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tif (value.length > maxValueLength) {\n\t\t\t\t\talert(`Tag value length must be <= ${maxValueLength}`);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\ttags[key] = value;\n\t\t\t\tif (Object.keys(tags).length > maxTags) {\n\t\t\t\t\talert(`Too many tags. Max ${maxTags} tags allowed.`);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tags;\n\t\t}\n\n\t\tasync function openS3TablesTags(resourceArn) {\n\t\t\tif (!resourceArn) return;\n\t\t\tdocument.getElementById('s3tablesTagsResourceArn').value = resourceArn;\n\t\t\tdocument.getElementById('s3tablesTagsInput').value = '';\n\t\t\tdocument.getElementById('s3tablesTagsDeleteInput').value = '';\n\t\t\tdocument.getElementById('s3tablesTagsList').textContent = 'Loading...';\n\t\t\ts3tablesTagsModal.show();\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(`/api/s3tables/tags?arn=${encodeURIComponent(resourceArn)}`);\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tdocument.getElementById('s3tablesTagsList').textContent = JSON.stringify(data.tags || {}, null, 2);\n\t\t\t\t} else {\n\t\t\t\t\tdocument.getElementById('s3tablesTagsList').textContent = data.error || 'Failed to load tags';\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tdocument.getElementById('s3tablesTagsList').textContent = error.message;\n\t\t\t}\n\t\t}\n\n\t\tasync function updateS3TablesTags(resourceArn, tags) {\n\t\t\ttry {\n\t\t\t\tconst response = await fetch('/api/s3tables/tags', {\n\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ resource_arn: resourceArn, tags: tags })\n\t\t\t\t});\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\talert(data.error || 'Failed to update tags');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\talert('Tags updated');\n\t\t\t\topenS3TablesTags(resourceArn);\n\t\t\t} catch (error) {\n\t\t\t\talert('Failed to update tags: ' + error.message);\n\t\t\t}\n\t\t}\n\n\t\tasync function deleteS3TablesTags() {\n\t\t\tconst resourceArn = document.getElementById('s3tablesTagsResourceArn').value;\n\t\t\tconst keysInput = document.getElementById('s3tablesTagsDeleteInput').value.trim();\n\t\t\tif (!resourceArn) return;\n\t\t\tconst tagKeys = keysInput.split(',').map(k => k.trim()).filter(k => k);\n\t\t\tif (tagKeys.length === 0) {\n\t\t\t\talert('Provide tag keys to remove');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst response = await fetch('/api/s3tables/tags', {\n\t\t\t\t\tmethod: 'DELETE',\n\t\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\t\tbody: JSON.stringify({ resource_arn: resourceArn, tag_keys: tagKeys })\n\t\t\t\t});\n\t\t\t\tconst data = await response.json();\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\talert(data.error || 'Failed to remove tags');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\talert('Tags removed');\n\t\t\t\topenS3TablesTags(resourceArn);\n\t\t\t} catch (error) {\n\t\t\t\talert('Failed to remove tags: ' + error.message);\n\t\t\t}\n\t\t}\n\t</script>")
|
|
if templ_7745c5c3_Err != nil {
|
|
return templ_7745c5c3_Err
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
var _ = templruntime.GeneratedTemplate
|