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.
		
		
		
		
		
			
		
			
				
					
					
						
							691 lines
						
					
					
						
							34 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							691 lines
						
					
					
						
							34 KiB
						
					
					
				
								package app
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
								    "fmt"
							 | 
						|
								    "github.com/seaweedfs/seaweedfs/weed/admin/dash"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
							 | 
						|
								    <div class="container-fluid">
							 | 
						|
								        <!-- Page Header -->
							 | 
						|
								        <div class="d-sm-flex align-items-center justify-content-between mb-4">
							 | 
						|
								            <div>
							 | 
						|
								                <h1 class="h3 mb-0 text-gray-800">
							 | 
						|
								                    <i class="fas fa-users me-2"></i>Object Store Users
							 | 
						|
								                </h1>
							 | 
						|
								                <p class="mb-0 text-muted">Manage S3 API users and their access credentials</p>
							 | 
						|
								            </div>
							 | 
						|
								            <div class="d-flex gap-2">
							 | 
						|
								                <button type="button" class="btn btn-primary" 
							 | 
						|
								                        data-bs-toggle="modal" 
							 | 
						|
								                        data-bs-target="#createUserModal">
							 | 
						|
								                    <i class="fas fa-plus me-1"></i>Create User
							 | 
						|
								                </button>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								
							 | 
						|
								        <!-- Summary Cards -->
							 | 
						|
								        <div class="row mb-4">
							 | 
						|
								            <div class="col-xl-3 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 Users
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="h5 mb-0 font-weight-bold text-gray-800">
							 | 
						|
								                                    {fmt.Sprintf("%d", data.TotalUsers)}
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="col-auto">
							 | 
						|
								                                <i class="fas fa-users fa-2x text-gray-300"></i>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="col-xl-3 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">
							 | 
						|
								                                    Total Users
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="h5 mb-0 font-weight-bold text-gray-800">
							 | 
						|
								                                    {fmt.Sprintf("%d", len(data.Users))}
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="col-auto">
							 | 
						|
								                                <i class="fas fa-user-check fa-2x text-gray-300"></i>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="col-xl-3 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>
							 | 
						|
								
							 | 
						|
								        <!-- Users Table -->
							 | 
						|
								        <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-users me-2"></i>Object Store Users
							 | 
						|
								                        </h6>
							 | 
						|
								                        <div class="dropdown no-arrow">
							 | 
						|
								                            <a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
							 | 
						|
								                                <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
							 | 
						|
								                            </a>
							 | 
						|
								                            <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in">
							 | 
						|
								                                <div class="dropdown-header">Actions:</div>
							 | 
						|
								                                <a class="dropdown-item" href="#" onclick="exportUsers()">
							 | 
						|
								                                    <i class="fas fa-download me-2"></i>Export List
							 | 
						|
								                                </a>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                    <div class="card-body">
							 | 
						|
								                        <div class="table-responsive">
							 | 
						|
								                            <table class="table table-hover" width="100%" cellspacing="0" id="usersTable">
							 | 
						|
								                                <thead>
							 | 
						|
								                                    <tr>
							 | 
						|
								                                        <th>Username</th>
							 | 
						|
								                                        <th>Email</th>
							 | 
						|
								                                        <th>Access Key</th>
							 | 
						|
								                                        <th>Actions</th>
							 | 
						|
								                                    </tr>
							 | 
						|
								                                </thead>
							 | 
						|
								                                <tbody>
							 | 
						|
								                                    for _, user := range data.Users {
							 | 
						|
								                                        <tr>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                <div class="d-flex align-items-center">
							 | 
						|
								                                                    <i class="fas fa-user me-2 text-muted"></i>
							 | 
						|
								                                                    <strong>{user.Username}</strong>
							 | 
						|
								                                                </div>
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>{user.Email}</td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                <code class="text-muted">{user.AccessKey}</code>
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                <div class="btn-group btn-group-sm" role="group">
							 | 
						|
								                                                    <button type="button" class="btn btn-outline-info" 
							 | 
						|
								                                                            data-action="show-user-details" data-username={ user.Username }>
							 | 
						|
								                                                        <i class="fas fa-info-circle"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                    <button type="button" class="btn btn-outline-primary" 
							 | 
						|
								                                                            data-action="edit-user" data-username={ user.Username }>
							 | 
						|
								                                                        <i class="fas fa-edit"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                    <button type="button" class="btn btn-outline-secondary" 
							 | 
						|
								                                                            data-action="manage-access-keys" data-username={ user.Username }>
							 | 
						|
								                                                        <i class="fas fa-key"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                    <button type="button" class="btn btn-outline-danger" 
							 | 
						|
								                                                            data-action="delete-user" data-username={ user.Username }>
							 | 
						|
								                                                        <i class="fas fa-trash"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                </div>
							 | 
						|
								                                            </td>
							 | 
						|
								                                        </tr>
							 | 
						|
								                                    }
							 | 
						|
								                                    if len(data.Users) == 0 {
							 | 
						|
								                                        <tr>
							 | 
						|
								                                            <td colspan="4" class="text-center text-muted py-4">
							 | 
						|
								                                                <i class="fas fa-users fa-3x mb-3 text-muted"></i>
							 | 
						|
								                                                <div>
							 | 
						|
								                                                    <h5>No users found</h5>
							 | 
						|
								                                                    <p>Create your first object store user to get started.</p>
							 | 
						|
								                                                </div>
							 | 
						|
								                                            </td>
							 | 
						|
								                                        </tr>
							 | 
						|
								                                    }
							 | 
						|
								                                </tbody>
							 | 
						|
								                            </table>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								
							 | 
						|
								        <!-- Last Updated -->
							 | 
						|
								        <div class="row">
							 | 
						|
								            <div class="col-12">
							 | 
						|
								                <small class="text-muted">
							 | 
						|
								                    <i class="fas fa-clock me-1"></i>
							 | 
						|
								                    Last updated: {data.LastUpdated.Format("2006-01-02 15:04:05")}
							 | 
						|
								                </small>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Create User Modal -->
							 | 
						|
								    <div class="modal fade" id="createUserModal" tabindex="-1" role="dialog">
							 | 
						|
								        <div class="modal-dialog" role="document">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title">
							 | 
						|
								                        <i class="fas fa-user-plus me-2"></i>Create New User
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body">
							 | 
						|
								                    <form id="createUserForm">
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="username" class="form-label">Username *</label>
							 | 
						|
								                            <input type="text" class="form-control" id="username" name="username" required>
							 | 
						|
								                        </div>
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="email" class="form-label">Email</label>
							 | 
						|
								                            <input type="email" class="form-control" id="email" name="email">
							 | 
						|
								                        </div>
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="actions" class="form-label">Permissions</label>
							 | 
						|
								                            <select multiple class="form-control" id="actions" name="actions" size="10">
							 | 
						|
								                                <option value="Admin">Admin (Full Access)</option>
							 | 
						|
								                                <option value="Read">Read</option>
							 | 
						|
								                                <option value="Write">Write</option>
							 | 
						|
								                                <option value="List">List</option>
							 | 
						|
								                                <option value="Tagging">Tagging</option>
							 | 
						|
								                                <optgroup label="Object Lock Permissions">
							 | 
						|
								                                    <option value="BypassGovernanceRetention">Bypass Governance Retention</option>
							 | 
						|
								                                    <option value="GetObjectRetention">Get Object Retention</option>
							 | 
						|
								                                    <option value="PutObjectRetention">Put Object Retention</option>
							 | 
						|
								                                    <option value="GetObjectLegalHold">Get Object Legal Hold</option>
							 | 
						|
								                                    <option value="PutObjectLegalHold">Put Object Legal Hold</option>
							 | 
						|
								                                    <option value="GetBucketObjectLockConfiguration">Get Bucket Object Lock Configuration</option>
							 | 
						|
								                                    <option value="PutBucketObjectLockConfiguration">Put Bucket Object Lock Configuration</option>
							 | 
						|
								                                </optgroup>
							 | 
						|
								                            </select>
							 | 
						|
								                            <small class="form-text text-muted">Hold Ctrl/Cmd to select multiple permissions</small>
							 | 
						|
								                        </div>
							 | 
						|
								                        <div class="mb-3 form-check">
							 | 
						|
								                            <input type="checkbox" class="form-check-input" id="generateKey" name="generateKey" checked>
							 | 
						|
								                            <label class="form-check-label" for="generateKey">
							 | 
						|
								                                Generate access key automatically
							 | 
						|
								                            </label>
							 | 
						|
								                        </div>
							 | 
						|
								                    </form>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
							 | 
						|
								                    <button type="button" class="btn btn-primary" onclick="handleCreateUser()">Create User</button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Edit User Modal -->
							 | 
						|
								    <div class="modal fade" id="editUserModal" tabindex="-1" role="dialog">
							 | 
						|
								        <div class="modal-dialog" role="document">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title">
							 | 
						|
								                        <i class="fas fa-user-edit me-2"></i>Edit User
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body">
							 | 
						|
								                    <form id="editUserForm">
							 | 
						|
								                        <input type="hidden" id="editUsername" name="username">
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="editEmail" class="form-label">Email</label>
							 | 
						|
								                            <input type="email" class="form-control" id="editEmail" name="email">
							 | 
						|
								                        </div>
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="editActions" class="form-label">Permissions</label>
							 | 
						|
								                            <select multiple class="form-control" id="editActions" name="actions" size="10">
							 | 
						|
								                                <option value="Admin">Admin (Full Access)</option>
							 | 
						|
								                                <option value="Read">Read</option>
							 | 
						|
								                                <option value="Write">Write</option>
							 | 
						|
								                                <option value="List">List</option>
							 | 
						|
								                                <option value="Tagging">Tagging</option>
							 | 
						|
								                                <optgroup label="Object Lock Permissions">
							 | 
						|
								                                    <option value="BypassGovernanceRetention">Bypass Governance Retention</option>
							 | 
						|
								                                    <option value="GetObjectRetention">Get Object Retention</option>
							 | 
						|
								                                    <option value="PutObjectRetention">Put Object Retention</option>
							 | 
						|
								                                    <option value="GetObjectLegalHold">Get Object Legal Hold</option>
							 | 
						|
								                                    <option value="PutObjectLegalHold">Put Object Legal Hold</option>
							 | 
						|
								                                    <option value="GetBucketObjectLockConfiguration">Get Bucket Object Lock Configuration</option>
							 | 
						|
								                                    <option value="PutBucketObjectLockConfiguration">Put Bucket Object Lock Configuration</option>
							 | 
						|
								                                </optgroup>
							 | 
						|
								                            </select>
							 | 
						|
								                        </div>
							 | 
						|
								                    </form>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
							 | 
						|
								                    <button type="button" class="btn btn-primary" onclick="handleUpdateUser()">Update User</button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- User Details Modal -->
							 | 
						|
								    <div class="modal fade" id="userDetailsModal" tabindex="-1" role="dialog">
							 | 
						|
								        <div class="modal-dialog modal-lg" role="document">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title">
							 | 
						|
								                        <i class="fas fa-user me-2"></i>User Details
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body" id="userDetailsContent">
							 | 
						|
								                    <!-- Content will be loaded dynamically -->
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Access Keys Management Modal -->
							 | 
						|
								    <div class="modal fade" id="accessKeysModal" tabindex="-1" role="dialog">
							 | 
						|
								        <div class="modal-dialog modal-lg" role="document">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title">
							 | 
						|
								                        <i class="fas fa-key me-2"></i>Manage Access Keys
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body">
							 | 
						|
								                    <div class="d-flex justify-content-between align-items-center mb-3">
							 | 
						|
								                        <h6>Access Keys for <span id="accessKeysUsername"></span></h6>
							 | 
						|
								                        <button type="button" class="btn btn-primary btn-sm" onclick="createAccessKey()">
							 | 
						|
								                            <i class="fas fa-plus me-1"></i>Create New Key
							 | 
						|
								                        </button>
							 | 
						|
								                    </div>
							 | 
						|
								                    <div id="accessKeysContent">
							 | 
						|
								                        <!-- Content will be loaded dynamically -->
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- JavaScript for user management -->
							 | 
						|
								    <script>
							 | 
						|
								        document.addEventListener('DOMContentLoaded', function() {
							 | 
						|
								            // Event delegation for user action buttons
							 | 
						|
								            document.addEventListener('click', function(e) {
							 | 
						|
								                const button = e.target.closest('[data-action]');
							 | 
						|
								                if (!button) return;
							 | 
						|
								                
							 | 
						|
								                const action = button.getAttribute('data-action');
							 | 
						|
								                const username = button.getAttribute('data-username');
							 | 
						|
								                
							 | 
						|
								                switch (action) {
							 | 
						|
								                    case 'show-user-details':
							 | 
						|
								                        showUserDetails(username);
							 | 
						|
								                        break;
							 | 
						|
								                    case 'edit-user':
							 | 
						|
								                        editUser(username);
							 | 
						|
								                        break;
							 | 
						|
								                    case 'manage-access-keys':
							 | 
						|
								                        manageAccessKeys(username);
							 | 
						|
								                        break;
							 | 
						|
								                    case 'delete-user':
							 | 
						|
								                        deleteUser(username);
							 | 
						|
								                        break;
							 | 
						|
								                }
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Show user details modal
							 | 
						|
								        async function showUserDetails(username) {
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch(`/api/users/${username}`);
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    const user = await response.json();
							 | 
						|
								                    document.getElementById('userDetailsContent').innerHTML = createUserDetailsContent(user);
							 | 
						|
								                    const modal = new bootstrap.Modal(document.getElementById('userDetailsModal'));
							 | 
						|
								                    modal.show();
							 | 
						|
								                } else {
							 | 
						|
								                    showErrorMessage('Failed to load user details');
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error loading user details:', error);
							 | 
						|
								                showErrorMessage('Failed to load user details');
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Edit user function
							 | 
						|
								        async function editUser(username) {
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch(`/api/users/${username}`);
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    const user = await response.json();
							 | 
						|
								                    
							 | 
						|
								                    // Populate edit form
							 | 
						|
								                    document.getElementById('editUsername').value = username;
							 | 
						|
								                    document.getElementById('editEmail').value = user.email || '';
							 | 
						|
								                    
							 | 
						|
								                    // Set selected actions
							 | 
						|
								                    const actionsSelect = document.getElementById('editActions');
							 | 
						|
								                    Array.from(actionsSelect.options).forEach(option => {
							 | 
						|
								                        option.selected = user.actions && user.actions.includes(option.value);
							 | 
						|
								                    });
							 | 
						|
								                    
							 | 
						|
								                    // Show modal
							 | 
						|
								                    const modal = new bootstrap.Modal(document.getElementById('editUserModal'));
							 | 
						|
								                    modal.show();
							 | 
						|
								                } else {
							 | 
						|
								                    showErrorMessage('Failed to load user details');
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error loading user:', error);
							 | 
						|
								                showErrorMessage('Failed to load user details');
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Manage access keys function
							 | 
						|
								        async function manageAccessKeys(username) {
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch(`/api/users/${username}`);
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    const user = await response.json();
							 | 
						|
								                    document.getElementById('accessKeysUsername').textContent = username;
							 | 
						|
								                    document.getElementById('accessKeysContent').innerHTML = createAccessKeysContent(user);
							 | 
						|
								                    const modal = new bootstrap.Modal(document.getElementById('accessKeysModal'));
							 | 
						|
								                    modal.show();
							 | 
						|
								                } else {
							 | 
						|
								                    showErrorMessage('Failed to load access keys');
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error loading access keys:', error);
							 | 
						|
								                showErrorMessage('Failed to load access keys');
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Delete user function
							 | 
						|
								        async function deleteUser(username) {
							 | 
						|
								            if (confirm(`Are you sure you want to delete user "${username}"? This action cannot be undone.`)) {
							 | 
						|
								                try {
							 | 
						|
								                    const response = await fetch(`/api/users/${username}`, {
							 | 
						|
								                        method: 'DELETE'
							 | 
						|
								                    });
							 | 
						|
								                    
							 | 
						|
								                    if (response.ok) {
							 | 
						|
								                        showSuccessMessage('User deleted successfully');
							 | 
						|
								                        setTimeout(() => window.location.reload(), 1000);
							 | 
						|
								                    } else {
							 | 
						|
								                        const error = await response.json();
							 | 
						|
								                        showErrorMessage('Failed to delete user: ' + (error.error || 'Unknown error'));
							 | 
						|
								                    }
							 | 
						|
								                } catch (error) {
							 | 
						|
								                    console.error('Error deleting user:', error);
							 | 
						|
								                    showErrorMessage('Failed to delete user: ' + error.message);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Handle create user form submission
							 | 
						|
								        async function handleCreateUser() {
							 | 
						|
								            const form = document.getElementById('createUserForm');
							 | 
						|
								            const formData = new FormData(form);
							 | 
						|
								            
							 | 
						|
								            const userData = {
							 | 
						|
								                username: formData.get('username'),
							 | 
						|
								                email: formData.get('email'),
							 | 
						|
								                actions: Array.from(document.getElementById('actions').selectedOptions).map(option => option.value),
							 | 
						|
								                generate_key: document.getElementById('generateKey').checked
							 | 
						|
								            };
							 | 
						|
								            
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch('/api/users', {
							 | 
						|
								                    method: 'POST',
							 | 
						|
								                    headers: {
							 | 
						|
								                        'Content-Type': 'application/json',
							 | 
						|
								                    },
							 | 
						|
								                    body: JSON.stringify(userData)
							 | 
						|
								                });
							 | 
						|
								                
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    const result = await response.json();
							 | 
						|
								                    showSuccessMessage('User created successfully');
							 | 
						|
								                    
							 | 
						|
								                    // Show the created access key if generated
							 | 
						|
								                    if (result.user && result.user.access_key) {
							 | 
						|
								                        showNewAccessKeyModal(result.user);
							 | 
						|
								                    }
							 | 
						|
								                    
							 | 
						|
								                    // Close modal and refresh page
							 | 
						|
								                    const modal = bootstrap.Modal.getInstance(document.getElementById('createUserModal'));
							 | 
						|
								                    modal.hide();
							 | 
						|
								                    form.reset();
							 | 
						|
								                    setTimeout(() => window.location.reload(), 1000);
							 | 
						|
								                } else {
							 | 
						|
								                    const error = await response.json();
							 | 
						|
								                    showErrorMessage('Failed to create user: ' + (error.error || 'Unknown error'));
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error creating user:', error);
							 | 
						|
								                showErrorMessage('Failed to create user: ' + error.message);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Handle update user form submission
							 | 
						|
								        async function handleUpdateUser() {
							 | 
						|
								            const username = document.getElementById('editUsername').value;
							 | 
						|
								            const formData = new FormData(document.getElementById('editUserForm'));
							 | 
						|
								            
							 | 
						|
								            const userData = {
							 | 
						|
								                email: formData.get('email'),
							 | 
						|
								                actions: Array.from(document.getElementById('editActions').selectedOptions).map(option => option.value)
							 | 
						|
								            };
							 | 
						|
								            
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch(`/api/users/${username}`, {
							 | 
						|
								                    method: 'PUT',
							 | 
						|
								                    headers: {
							 | 
						|
								                        'Content-Type': 'application/json',
							 | 
						|
								                    },
							 | 
						|
								                    body: JSON.stringify(userData)
							 | 
						|
								                });
							 | 
						|
								                
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    showSuccessMessage('User updated successfully');
							 | 
						|
								                    
							 | 
						|
								                    // Close modal and refresh page
							 | 
						|
								                    const modal = bootstrap.Modal.getInstance(document.getElementById('editUserModal'));
							 | 
						|
								                    modal.hide();
							 | 
						|
								                    setTimeout(() => window.location.reload(), 1000);
							 | 
						|
								                } else {
							 | 
						|
								                    const error = await response.json();
							 | 
						|
								                    showErrorMessage('Failed to update user: ' + (error.error || 'Unknown error'));
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error updating user:', error);
							 | 
						|
								                showErrorMessage('Failed to update user: ' + error.message);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Create user details content
							 | 
						|
								        function createUserDetailsContent(user) {
							 | 
						|
								            var detailsHtml = '<div class="row">';
							 | 
						|
								            detailsHtml += '<div class="col-md-6">';
							 | 
						|
								            detailsHtml += '<h6 class="text-muted">Basic Information</h6>';
							 | 
						|
								            detailsHtml += '<table class="table table-sm">';
							 | 
						|
								            detailsHtml += '<tr><td><strong>Username:</strong></td><td>' + escapeHtml(user.username) + '</td></tr>';
							 | 
						|
								            detailsHtml += '<tr><td><strong>Email:</strong></td><td>' + escapeHtml(user.email || 'Not set') + '</td></tr>';
							 | 
						|
								            detailsHtml += '</table>';
							 | 
						|
								            detailsHtml += '</div>';
							 | 
						|
								            detailsHtml += '<div class="col-md-6">';
							 | 
						|
								                         detailsHtml += '<h6 class="text-muted">Permissions</h6>';
							 | 
						|
								             detailsHtml += '<div class="mb-3">';
							 | 
						|
								             if (user.actions && user.actions.length > 0) {
							 | 
						|
								                 detailsHtml += user.actions.map(function(action) {
							 | 
						|
								                     return '<span class="badge bg-info me-1">' + action + '</span>';
							 | 
						|
								                 }).join('');
							 | 
						|
								             } else {
							 | 
						|
								                 detailsHtml += '<span class="text-muted">No permissions assigned</span>';
							 | 
						|
								             }
							 | 
						|
								             detailsHtml += '</div>';
							 | 
						|
								             detailsHtml += '<h6 class="text-muted">Access Keys</h6>';
							 | 
						|
								             if (user.access_keys && user.access_keys.length > 0) {
							 | 
						|
								                 detailsHtml += '<div class="mb-2">';
							 | 
						|
								                 user.access_keys.forEach(function(key) {
							 | 
						|
								                     detailsHtml += '<div><code class="text-muted">' + key.access_key + '</code></div>';
							 | 
						|
								                 });
							 | 
						|
								                 detailsHtml += '</div>';
							 | 
						|
								             } else {
							 | 
						|
								                 detailsHtml += '<p class="text-muted">No access keys</p>';
							 | 
						|
								             }
							 | 
						|
								            detailsHtml += '</div>';
							 | 
						|
								            detailsHtml += '</div>';
							 | 
						|
								            return detailsHtml;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Create access keys content
							 | 
						|
								        function createAccessKeysContent(user) {
							 | 
						|
								            if (!user.access_keys || user.access_keys.length === 0) {
							 | 
						|
								                return '<p class="text-muted">No access keys available</p>';
							 | 
						|
								            }
							 | 
						|
								            
							 | 
						|
								            var keysHtml = '<div class="table-responsive">';
							 | 
						|
								            keysHtml += '<table class="table table-sm">';
							 | 
						|
								            keysHtml += '<thead><tr><th>Access Key</th><th>Status</th><th>Actions</th></tr></thead>';
							 | 
						|
								            keysHtml += '<tbody>';
							 | 
						|
								            
							 | 
						|
								            user.access_keys.forEach(function(key) {
							 | 
						|
								                keysHtml += '<tr>';
							 | 
						|
								                keysHtml += '<td><code>' + key.access_key + '</code></td>';
							 | 
						|
								                keysHtml += '<td><span class="badge bg-success">Active</span></td>';
							 | 
						|
								                keysHtml += '<td>';
							 | 
						|
								                keysHtml += '<button class="btn btn-outline-danger btn-sm" onclick="deleteAccessKey(\'' + user.username + '\', \'' + key.access_key + '\')">';
							 | 
						|
								                keysHtml += '<i class="fas fa-trash"></i> Delete';
							 | 
						|
								                keysHtml += '</button>';
							 | 
						|
								                keysHtml += '</td>';
							 | 
						|
								                keysHtml += '</tr>';
							 | 
						|
								            });
							 | 
						|
								            
							 | 
						|
								            keysHtml += '</tbody>';
							 | 
						|
								            keysHtml += '</table>';
							 | 
						|
								            keysHtml += '</div>';
							 | 
						|
								            return keysHtml;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Create new access key
							 | 
						|
								        async function createAccessKey() {
							 | 
						|
								            const username = document.getElementById('accessKeysUsername').textContent;
							 | 
						|
								            
							 | 
						|
								            try {
							 | 
						|
								                const response = await fetch(`/api/users/${username}/access-keys`, {
							 | 
						|
								                    method: 'POST',
							 | 
						|
								                    headers: {
							 | 
						|
								                        'Content-Type': 'application/json',
							 | 
						|
								                    },
							 | 
						|
								                    body: JSON.stringify({})
							 | 
						|
								                });
							 | 
						|
								                
							 | 
						|
								                if (response.ok) {
							 | 
						|
								                    const result = await response.json();
							 | 
						|
								                    showSuccessMessage('Access key created successfully');
							 | 
						|
								                    
							 | 
						|
								                    // Refresh access keys display
							 | 
						|
								                    const userResponse = await fetch(`/api/users/${username}`);
							 | 
						|
								                    if (userResponse.ok) {
							 | 
						|
								                        const user = await userResponse.json();
							 | 
						|
								                        document.getElementById('accessKeysContent').innerHTML = createAccessKeysContent(user);
							 | 
						|
								                    }
							 | 
						|
								                } else {
							 | 
						|
								                    const error = await response.json();
							 | 
						|
								                    showErrorMessage('Failed to create access key: ' + (error.error || 'Unknown error'));
							 | 
						|
								                }
							 | 
						|
								            } catch (error) {
							 | 
						|
								                console.error('Error creating access key:', error);
							 | 
						|
								                showErrorMessage('Failed to create access key: ' + error.message);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Delete access key
							 | 
						|
								        async function deleteAccessKey(username, accessKey) {
							 | 
						|
								            if (confirm('Are you sure you want to delete this access key?')) {
							 | 
						|
								                try {
							 | 
						|
								                    const response = await fetch(`/api/users/${username}/access-keys/${accessKey}`, {
							 | 
						|
								                        method: 'DELETE'
							 | 
						|
								                    });
							 | 
						|
								                    
							 | 
						|
								                    if (response.ok) {
							 | 
						|
								                        showSuccessMessage('Access key deleted successfully');
							 | 
						|
								                        
							 | 
						|
								                        // Refresh access keys display
							 | 
						|
								                        const userResponse = await fetch(`/api/users/${username}`);
							 | 
						|
								                        if (userResponse.ok) {
							 | 
						|
								                            const user = await userResponse.json();
							 | 
						|
								                            document.getElementById('accessKeysContent').innerHTML = createAccessKeysContent(user);
							 | 
						|
								                        }
							 | 
						|
								                    } else {
							 | 
						|
								                        const error = await response.json();
							 | 
						|
								                        showErrorMessage('Failed to delete access key: ' + (error.error || 'Unknown error'));
							 | 
						|
								                    }
							 | 
						|
								                } catch (error) {
							 | 
						|
								                    console.error('Error deleting access key:', error);
							 | 
						|
								                    showErrorMessage('Failed to delete access key: ' + error.message);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Show new access key modal (when user is created with generated key)
							 | 
						|
								        function showNewAccessKeyModal(user) {
							 | 
						|
								            // Create a simple alert for now - could be enhanced with a dedicated modal
							 | 
						|
								            var message = 'New user created!\n\n';
							 | 
						|
								            message += 'Username: ' + user.username + '\n';
							 | 
						|
								            message += 'Access Key: ' + user.access_key + '\n';
							 | 
						|
								            message += 'Secret Key: ' + user.secret_key + '\n\n';
							 | 
						|
								            message += 'Please save these credentials securely.';
							 | 
						|
								            alert(message);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Utility functions
							 | 
						|
								        function showSuccessMessage(message) {
							 | 
						|
								            // Simple implementation - could be enhanced with toast notifications
							 | 
						|
								            alert('Success: ' + message);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        function showErrorMessage(message) {
							 | 
						|
								            // Simple implementation - could be enhanced with toast notifications
							 | 
						|
								            alert('Error: ' + message);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        function escapeHtml(text) {
							 | 
						|
								            if (!text) return '';
							 | 
						|
								            const div = document.createElement('div');
							 | 
						|
								            div.textContent = text;
							 | 
						|
								            return div.innerHTML;
							 | 
						|
								        }
							 | 
						|
								    </script>
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Helper functions for template
							 | 
						|
								 
							 |