665 lines
31 KiB
HTML
665 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>mhvtl Configuration Manager - Adastra VTL</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
|
||
<body>
|
||
<nav class="navbar">
|
||
<div class="container">
|
||
<div class="nav-brand">
|
||
<h1>🎞️ Adastra VTL</h1>
|
||
<span class="subtitle">Virtual Tape Library Configuration</span>
|
||
</div>
|
||
<div class="nav-links">
|
||
<a href="#system" class="nav-link">System</a>
|
||
<a href="#library" class="nav-link active">Library</a>
|
||
<a href="#drives" class="nav-link">Drives</a>
|
||
<a href="#tapes" class="nav-link">Tapes</a>
|
||
<a href="#manage-tapes" class="nav-link">Manage Tapes</a>
|
||
<a href="#iscsi" class="nav-link">iSCSI</a>
|
||
<a href="#users" class="nav-link" id="users-tab">Users</a>
|
||
<a href="#export" class="nav-link">Export</a>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
|
||
<main class="container">
|
||
<section id="system" class="section">
|
||
<div class="section-header">
|
||
<h2>🖥️ System Monitoring & Management</h2>
|
||
<p>Monitor system health and manage appliance power</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>💚 System Health Dashboard</h3>
|
||
<button class="btn btn-primary" onclick="refreshSystemHealth()">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="health-loading" style="display: none; text-align: center; padding: 2rem;">
|
||
<strong>⏳</strong> Loading system health...
|
||
</div>
|
||
<div id="health-dashboard">
|
||
<!-- Health dashboard will be populated here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>⚡ Power Management</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<p><strong>⚠️ Warning:</strong> These actions will affect the entire appliance.</p>
|
||
<div style="margin-top: 1rem;">
|
||
<button class="btn btn-warning" onclick="restartAppliance()" style="margin-right: 1rem;">
|
||
<span>🔄</span> Restart Appliance
|
||
</button>
|
||
<button class="btn btn-danger" onclick="shutdownAppliance()">
|
||
<span>⏻</span> Shutdown Appliance
|
||
</button>
|
||
</div>
|
||
<div id="power-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="library" class="section active">
|
||
<div class="section-header">
|
||
<h2>📚 Library Configuration</h2>
|
||
<p>Configure your virtual tape library settings</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Library Settings</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="lib-id">Library ID</label>
|
||
<input type="number" id="lib-id" value="10" min="0" max="99">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-channel">Channel</label>
|
||
<input type="number" id="lib-channel" value="0" min="0" max="15">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-target">Target</label>
|
||
<input type="number" id="lib-target" value="0" min="0" max="15">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-lun">LUN</label>
|
||
<input type="number" id="lib-lun" value="0" min="0" max="7">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-vendor">Vendor</label>
|
||
<input type="text" id="lib-vendor" value="STK" maxlength="8">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-product">Product</label>
|
||
<input type="text" id="lib-product" value="L700" maxlength="16">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-serial">Serial Number</label>
|
||
<input type="text" id="lib-serial" value="XYZZY_A" maxlength="10">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-naa">NAA</label>
|
||
<input type="text" id="lib-naa" value="10:22:33:44:ab:cd:ef:00" pattern="[0-9a-f:]+">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-home">Home Directory</label>
|
||
<input type="text" id="lib-home" value="/opt/mhvtl">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lib-backoff">Backoff (ms)</label>
|
||
<input type="number" id="lib-backoff" value="400" min="0" max="10000">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="drives" class="section">
|
||
<div class="section-header">
|
||
<h2>💾 Drive Configuration</h2>
|
||
<p>Manage virtual tape drives</p>
|
||
</div>
|
||
|
||
<div class="drives-container" id="drives-container">
|
||
</div>
|
||
|
||
<button class="btn btn-primary" onclick="addDrive()">
|
||
<span>➕</span> Add Drive
|
||
</button>
|
||
</section>
|
||
|
||
<section id="tapes" class="section">
|
||
<div class="section-header">
|
||
<h2>📼 Tape Configuration</h2>
|
||
<p>Configure virtual tape media</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Tape Generation Settings</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="tape-library">Library ID</label>
|
||
<input type="number" id="tape-library" value="10" min="0" max="99">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-barcode-prefix">Barcode Prefix</label>
|
||
<input type="text" id="tape-barcode-prefix" value="CLN" maxlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-start-num">Starting Number</label>
|
||
<input type="number" id="tape-start-num" value="100" min="1" max="9999">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-size">Tape Size (MB)</label>
|
||
<input type="number" id="tape-size" value="2500000" min="1000" max="10000000">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-media-type">Media Type</label>
|
||
<select id="tape-media-type">
|
||
<option value="data">Data</option>
|
||
<option value="clean">Cleaning</option>
|
||
<option value="WORM">WORM</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-density">Density</label>
|
||
<select id="tape-density">
|
||
<option value="LTO5">LTO-5</option>
|
||
<option value="LTO6" selected>LTO-6</option>
|
||
<option value="LTO7">LTO-7</option>
|
||
<option value="LTO8">LTO-8</option>
|
||
<option value="LTO9">LTO-9</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="tape-count">Number of Tapes</label>
|
||
<input type="number" id="tape-count" value="20" min="1" max="1000">
|
||
</div>
|
||
</div>
|
||
<div class="alert alert-info">
|
||
<strong>ℹ️ Info:</strong> Generate mktape commands for creating virtual tapes. Run these
|
||
commands on the server after installation.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="manage-tapes" class="section">
|
||
<div class="section-header">
|
||
<h2>🗂️ Manage Virtual Tapes</h2>
|
||
<p>Complete CRUD management for virtual tape files</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>👀 Library Visualizer</h3>
|
||
<button class="btn btn-primary btn-small" onclick="loadLibraryStatus()" style="float: right;">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="viz-loading" style="display: none; text-align: center; padding: 2rem;">
|
||
<strong>⏳</strong> Loading library status...
|
||
</div>
|
||
<div id="viz-error" class="alert alert-danger" style="display: none;"></div>
|
||
<div id="library-viz" class="library-viz" style="display: none;">
|
||
<div class="viz-section">
|
||
<h4>Drives</h4>
|
||
<div id="viz-drives" class="viz-grid"></div>
|
||
</div>
|
||
<div class="viz-section">
|
||
<h4>MAPs / Ports</h4>
|
||
<div id="viz-maps" class="viz-grid"></div>
|
||
</div>
|
||
<div class="viz-section">
|
||
<h4>Storage Slots</h4>
|
||
<div id="viz-slots" class="viz-grid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>➕ Create New Tapes</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="create-library">Library Number</label>
|
||
<input type="number" id="create-library" value="10" min="1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-barcode-prefix">Barcode Prefix</label>
|
||
<input type="text" id="create-barcode-prefix" value="CLN" maxlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-start-num">Starting Number</label>
|
||
<input type="number" id="create-start-num" value="100" min="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-count">Number of Tapes</label>
|
||
<input type="number" id="create-count" value="1" min="1" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-size">Tape Size (MB)</label>
|
||
<input type="number" id="create-size" value="2500000" min="1000">
|
||
<small>Default: 2.5TB = 2,500,000 MB</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-media-type">Media Type</label>
|
||
<select id="create-media-type">
|
||
<option value="data">Data</option>
|
||
<option value="clean">Cleaning</option>
|
||
<option value="WORM">WORM</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="create-density">Density</label>
|
||
<select id="create-density">
|
||
<option value="LTO5">LTO-5</option>
|
||
<option value="LTO6" selected>LTO-6</option>
|
||
<option value="LTO7">LTO-7</option>
|
||
<option value="LTO8">LTO-8</option>
|
||
<option value="LTO9">LTO-9</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 1rem;">
|
||
<button class="btn btn-success" onclick="createTapes()">
|
||
<span>➕</span> Create Tapes
|
||
</button>
|
||
<div id="create-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>📋 Tape Files</h3>
|
||
<button class="btn btn-primary" onclick="loadTapeList()">
|
||
<span>🔄</span> Refresh List
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="tape-list-loading" style="display: none; text-align: center; padding: 2rem;">
|
||
<strong>⏳</strong> Loading tape files...
|
||
</div>
|
||
<div id="tape-list-error" class="alert alert-danger" style="display: none;"></div>
|
||
<div id="tape-list-empty" class="alert alert-info" style="display: none;">
|
||
<strong>ℹ️</strong> No tape files found. Create tapes using the commands from the "Tapes"
|
||
section.
|
||
</div>
|
||
<div id="tape-list-container" style="display: none;">
|
||
<div style="margin-bottom: 1rem;">
|
||
<input type="text" id="tape-search" placeholder="🔍 Search tapes..."
|
||
style="width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;"
|
||
onkeyup="filterTapes()">
|
||
</div>
|
||
<table class="tape-table" id="tape-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Barcode</th>
|
||
<th>Size</th>
|
||
<th>Modified</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tape-list-body">
|
||
</tbody>
|
||
</table>
|
||
<div style="margin-top: 1rem; padding: 1rem; background: #f8f9fa; border-radius: 4px;">
|
||
<strong>Total Tapes:</strong> <span id="tape-count-display">0</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>Bulk Actions</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<p>Delete multiple tapes at once. Use with caution!</p>
|
||
<div class="form-group">
|
||
<label for="bulk-delete-pattern">Delete Pattern (e.g., CLN*)</label>
|
||
<input type="text" id="bulk-delete-pattern" placeholder="CLN*">
|
||
</div>
|
||
<button class="btn btn-danger" onclick="bulkDeleteTapes()">
|
||
<span>🗑️</span> Bulk Delete
|
||
</button>
|
||
<div id="bulk-delete-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="iscsi" class="section">
|
||
<div class="section-header">
|
||
<h2>🔌 iSCSI Target Management</h2>
|
||
<p>Manage iSCSI targets, initiators, and LUNs</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>💾 Device Mapping</h3>
|
||
<button class="btn btn-primary" onclick="loadDeviceMapping()">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<p>Current SCSI devices available for iSCSI export:</p>
|
||
<div id="device-mapping-loading" style="display: none; text-align: center; padding: 2rem;">
|
||
<strong>⏳</strong> Loading device mapping...
|
||
</div>
|
||
<div id="device-mapping">
|
||
<!-- Device mapping will be populated here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>🎯 iSCSI Targets</h3>
|
||
<button class="btn btn-primary" onclick="loadTargets()">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="target-list-empty" class="alert alert-info">
|
||
<strong>ℹ️</strong> No targets configured. Create a target below.
|
||
</div>
|
||
<div id="target-list-container" style="display: none;">
|
||
<table class="tape-table" id="target-table">
|
||
<thead>
|
||
<tr>
|
||
<th>TID</th>
|
||
<th>Target Name (IQN)</th>
|
||
<th>LUNs</th>
|
||
<th>ACLs</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="target-list-body">
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>👥 Active Client Sessions</h3>
|
||
<button class="btn btn-primary btn-small" onclick="loadTargets()">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<table class="tape-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Target</th>
|
||
<th>Initiator Name (Client)</th>
|
||
<th>IP Address</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="iscsi-sessions-body">
|
||
<!-- JS populates this -->
|
||
</tbody>
|
||
</table>
|
||
<div id="no-sessions-msg" class="alert alert-info" style="display: none; margin-top: 0.5rem;">
|
||
No active sessions found.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>➕ Create New Target</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="target-tid">Target ID (TID)</label>
|
||
<input type="number" id="target-tid" value="1" min="1">
|
||
<small>Unique target identifier</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="target-name">Target Name</label>
|
||
<input type="text" id="target-name" placeholder="vtl.drive0">
|
||
<small>Will be: iqn.2024-01.com.vtl-linux:<name></small>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-success" onclick="createTarget()">
|
||
<span>➕</span> Create Target
|
||
</button>
|
||
<div id="create-target-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>💾 Add LUN (Backing Store)</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="lun-tid">Target ID</label>
|
||
<input type="number" id="lun-tid" value="1" min="1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lun-number">LUN Number</label>
|
||
<input type="number" id="lun-number" value="1" min="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="lun-device">Device Path</label>
|
||
<input type="text" id="lun-device" placeholder="/dev/sg1">
|
||
<small>SCSI generic device (e.g., /dev/sg1, /dev/sg2)</small>
|
||
</div>
|
||
</div>
|
||
<button class="btn btn-success" onclick="addLun()">
|
||
<span>➕</span> Add LUN
|
||
</button>
|
||
<div id="add-lun-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>🔐 Manage Initiator ACLs</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="acl-tid">Target ID</label>
|
||
<input type="number" id="acl-tid" value="1" min="1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="acl-address">Initiator Address</label>
|
||
<input type="text" id="acl-address" placeholder="192.168.1.100 or ALL">
|
||
<small>IP address or "ALL" for any initiator</small>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 1rem;">
|
||
<button class="btn btn-success" onclick="bindInitiator()">
|
||
<span>✅</span> Allow Initiator
|
||
</button>
|
||
<button class="btn btn-danger" onclick="unbindInitiator()">
|
||
<span>🚫</span> Block Initiator
|
||
</button>
|
||
</div>
|
||
<div id="acl-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="users" class="section">
|
||
<div class="section-header">
|
||
<h2>👥 User Management</h2>
|
||
<p>Manage user accounts and permissions (Admin Only)</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>📋 User List</h3>
|
||
<button class="btn btn-primary" onclick="loadUsers()">
|
||
<span>🔄</span> Refresh
|
||
</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="users-loading" style="display: none; text-align: center; padding: 2rem;">
|
||
<strong>⏳</strong> Loading users...
|
||
</div>
|
||
<div id="users-list">
|
||
<!-- Users will be populated here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>➕ Create New User</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="new-username">Username</label>
|
||
<input type="text" id="new-username" placeholder="Enter username" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new-password">Password</label>
|
||
<input type="password" id="new-password" placeholder="Enter password" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new-role">Role</label>
|
||
<select id="new-role">
|
||
<option value="viewer">Viewer (Read-Only)</option>
|
||
<option value="admin">Admin (Full Access)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 1rem;">
|
||
<button class="btn btn-success" onclick="createNewUser()">
|
||
<span>➕</span> Create User
|
||
</button>
|
||
</div>
|
||
<div id="create-user-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>🔑 Change Password</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<p>Change your own password</p>
|
||
<div class="form-grid">
|
||
<div class="form-group">
|
||
<label for="current-password">Current Password</label>
|
||
<input type="password" id="current-password" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new-user-password">New Password</label>
|
||
<input type="password" id="new-user-password" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="confirm-password">Confirm New Password</label>
|
||
<input type="password" id="confirm-password" required>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top: 1rem;">
|
||
<button class="btn btn-warning" onclick="changeUserPassword()">
|
||
<span>🔑</span> Change Password
|
||
</button>
|
||
</div>
|
||
<div id="change-password-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="export" class="section">
|
||
<div class="section-header">
|
||
<h2>📤 Export Configuration</h2>
|
||
<p>Generate and download configuration files</p>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Configuration Preview</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<pre id="config-preview" class="config-preview"></pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="button-group">
|
||
<button class="btn btn-primary" onclick="generateConfig()">
|
||
<span>🔄</span> Generate Config
|
||
</button>
|
||
<button class="btn btn-success" onclick="applyConfig()">
|
||
<span>💾</span> Apply to Server
|
||
</button>
|
||
<button class="btn btn-success" onclick="downloadConfig()">
|
||
<span>⬇️</span> Download device.conf
|
||
</button>
|
||
<button class="btn btn-secondary" onclick="copyConfig()">
|
||
<span>📋</span> Copy to Clipboard
|
||
</button>
|
||
</div>
|
||
|
||
<div id="apply-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
|
||
<div class="card" style="margin-top: 1rem;">
|
||
<div class="card-header">
|
||
<h3>Service Management</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<p>After applying configuration, restart the mhvtl service to apply changes.</p>
|
||
<button class="btn btn-warning" onclick="restartService()">
|
||
<span>🔄</span> Restart mhvtl Service
|
||
</button>
|
||
<div id="restart-result" class="alert" style="display: none; margin-top: 1rem;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Installation Command</h3>
|
||
</div>
|
||
<div class="card-body">
|
||
<pre id="install-command" class="config-preview"></pre>
|
||
<button class="btn btn-secondary" onclick="copyInstallCommand()">
|
||
<span>📋</span> Copy Command
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="footer">
|
||
<div class="container">
|
||
<p>© Adastra Visi Teknologi • <a href="http://adastra.id">adastra.id</a> • mhvtl Configuration Manager</p>
|
||
</div>
|
||
</footer>
|
||
|
||
<script src="script.js"></script>
|
||
</body>
|
||
|
||
</html> |