Add docker-ip-addr-manager initial app
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Docker IP Addr Manager</title>
|
||||
<link rel="stylesheet" href="/static/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main id="app" class="app-shell" v-cloak>
|
||||
<header class="topbar">
|
||||
<div>
|
||||
<h1>Docker IP Addr Manager</h1>
|
||||
<p>Manage host IP addresses used for ZimaOS port bindings.</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" @click="refreshEntries" :disabled="loading">
|
||||
{{ loading ? 'Refreshing...' : 'Refresh' }}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Add IP Entry</h2>
|
||||
<form class="entry-form" @submit.prevent="createEntry">
|
||||
<label>
|
||||
Name
|
||||
<input v-model.trim="form.name" type="text" required maxlength="96" />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
IP Address
|
||||
<input v-model.trim="form.ip" type="text" placeholder="10.0.4.2" required />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
CIDR
|
||||
<input v-model.number="form.cidr" type="number" min="0" max="32" required />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Device
|
||||
<select v-model="form.device" required>
|
||||
<option disabled value="">Choose device</option>
|
||||
<option v-for="iface in interfaces" :key="iface" :value="iface">{{ iface }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button class="btn btn-primary" type="submit" :disabled="saving">Add</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="status" v-if="errorMessage">{{ errorMessage }}</div>
|
||||
<div class="status warning" v-if="!usageKnown">
|
||||
Docker usage status is unknown. Disable/Delete operations are fail-closed until refresh succeeds.
|
||||
<span v-if="usageError">Error: {{ usageError }}</span>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th @click="setSort('name')">Name <span>{{ sortIndicator('name') }}</span></th>
|
||||
<th @click="setSort('ip')">IP Address <span>{{ sortIndicator('ip') }}</span></th>
|
||||
<th @click="setSort('cidr')">CIDR <span>{{ sortIndicator('cidr') }}</span></th>
|
||||
<th @click="setSort('used')">Used <span>{{ sortIndicator('used') }}</span></th>
|
||||
<th @click="setSort('containers')">Container(s) <span>{{ sortIndicator('containers') }}</span></th>
|
||||
<th @click="setSort('device')">Device <span>{{ sortIndicator('device') }}</span></th>
|
||||
<th @click="setSort('enabled')">Enabled <span>{{ sortIndicator('enabled') }}</span></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="entry in sortedEntries" :key="entry.id">
|
||||
<td><input v-model.trim="entry.draft.name" type="text" maxlength="96" :disabled="entry.enabled" /></td>
|
||||
<td><input v-model.trim="entry.draft.ip" type="text" :disabled="entry.enabled" /></td>
|
||||
<td><input v-model.number="entry.draft.cidr" type="number" min="0" max="32" :disabled="entry.enabled" /></td>
|
||||
<td class="status-cell">
|
||||
<span v-if="!entry.usage_known" class="chip unknown">Unknown</span>
|
||||
<span v-else-if="entry.used" class="chip used">Yes</span>
|
||||
<span v-else class="chip free">No</span>
|
||||
</td>
|
||||
<td class="containers">{{ containerLabel(entry) }}</td>
|
||||
<td>
|
||||
<select v-model="entry.draft.device" :disabled="entry.enabled">
|
||||
<option v-for="iface in interfaces" :key="`${entry.id}-${iface}`" :value="iface">{{ iface }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="toggle-cell">
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
:class="entry.enabled ? 'btn-danger' : 'btn-primary'"
|
||||
@click="toggleEntry(entry)"
|
||||
:disabled="isBusy(entry.id)"
|
||||
>
|
||||
{{ entry.enabled ? 'Disable' : 'Enable' }}
|
||||
</button>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
@click="saveEntry(entry)"
|
||||
:disabled="entry.enabled || isBusy(entry.id)"
|
||||
>Save</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@click="deleteEntry(entry)"
|
||||
:disabled="entry.enabled || isBusy(entry.id)"
|
||||
>Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="sortedEntries.length === 0">
|
||||
<td colspan="8" class="empty">No entries configured.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||
<script src="/static/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user