226 lines
6.3 KiB
Svelte
226 lines
6.3 KiB
Svelte
<script>
|
|
import { onMount } from "svelte";
|
|
import { backendUrl, saslAuthMethods } from "../../lib/constants.js";
|
|
import { apiFetch } from "../../utils.js";
|
|
|
|
const jsonRequest = type => async (path, object) => fetch(backendUrl + path, {
|
|
method: type,
|
|
body: JSON.stringify(object),
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
|
|
const findLast = array => {
|
|
if (!array || array.length === 0) {
|
|
return undefined
|
|
}
|
|
return array[array.length - 1]
|
|
}
|
|
|
|
const deepCopy = object => JSON.parse(JSON.stringify(object))
|
|
|
|
const post = jsonRequest('POST')
|
|
const put = jsonRequest('PUT')
|
|
|
|
const addCluster = async cluster => fetchClusters(await post('/clusters', cluster))
|
|
const updateCluster = async cluster => fetchClusters(await put('/clusters', cluster))
|
|
const deleteCluster = async clusterName => fetchClusters(await post('/clusters/delete', { clusterName }))
|
|
|
|
let clusters = []
|
|
const fetchClusters = async (response) => {
|
|
if (response) {
|
|
clusters = await response.json()
|
|
} else {
|
|
clusters = await apiFetch('/clusters')
|
|
}
|
|
clusters = Object.fromEntries(
|
|
Object.entries(clusters).map(([name, cluster]) =>
|
|
[name, ({ ...cluster, clusterName: name, originalName: name })]))
|
|
|
|
const values = Object.values(clusters)
|
|
if (values.length > 0) {
|
|
config = deepCopy(values[0])
|
|
}
|
|
}
|
|
onMount(async () => {
|
|
await fetchClusters()
|
|
console.log('onMount', clusters)
|
|
})
|
|
console.log('instant', clusters)
|
|
|
|
const startNewCluster = () => {
|
|
config = emptyConfig()
|
|
}
|
|
|
|
const emptyConfig = () => ({
|
|
originalName: null, // A `null` originalName indicates that the config is new,
|
|
clusterName: '', // and will be POSTed as a new cluster, not PUTted, updating an existing one
|
|
clientId: '',
|
|
brokers: [''],
|
|
ssl: true,
|
|
sasl: {
|
|
mechanism: '',
|
|
username: '',
|
|
password: ''
|
|
}
|
|
})
|
|
let config = emptyConfig()
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<meta name="description" content="Kafka Dance Settings"/>
|
|
</svelte:head>
|
|
|
|
<section>
|
|
<div class="query-settings">
|
|
<h1>Cluster Configuration</h1>
|
|
|
|
<div class="settings-option">
|
|
Cluster Name: <input bind:value={config.clusterName}>
|
|
</div>
|
|
|
|
<div class="settings-option">
|
|
Client Id: <input bind:value={config.clientId}>
|
|
</div>
|
|
|
|
<div class="settings-option">
|
|
Brokers:
|
|
{#each config.brokers as broker}
|
|
<input bind:value={broker}>
|
|
{/each}
|
|
{#if findLast(config.brokers) !== ''}
|
|
<button on:click={() => {config.brokers = [...config.brokers, '']}}>+</button>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="settings-option">
|
|
<div>
|
|
SSL:
|
|
<select bind:value={config.ssl}>
|
|
<option value={true}>True</option>
|
|
<option value={false}>False</option>
|
|
<option value={{}}>Advanced</option>
|
|
</select>
|
|
{#if typeof(config.ssl) === 'object'}
|
|
<div>
|
|
Reject Unauthorized
|
|
<input type="checkbox" bind:checked={config.ssl.rejectUnauthorized}>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TODO: Add options for SSL config -->
|
|
<div class="settings-option">
|
|
<div>SASL:</div>
|
|
<div class="settings-sub-option">
|
|
<div>Auth Mechanism:</div>
|
|
<select bind:value={config.sasl.mechanism}>
|
|
{#each saslAuthMethods as method}
|
|
<option value={method}>{method}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
<div class="settings-sub-option">
|
|
<div>Username:</div>
|
|
<input bind:value={config.sasl.username}>
|
|
</div>
|
|
<div class="settings-sub-option">
|
|
<div>Password:</div>
|
|
<input type="password" bind:value={config.sasl.password}>
|
|
</div>
|
|
</div>
|
|
|
|
<button style="float: right;"
|
|
on:click={() => (config.originalName ? updateCluster : addCluster)(config)}>{config.originalName ? 'Update Cluster Config' : 'Add Cluster Config'}</button>
|
|
</div>
|
|
|
|
<div class="data-view">
|
|
<button style="margin: 1em;" on:click={startNewCluster}>Add New Cluster</button>
|
|
{#each Object.entries(clusters) as [clusterName, cluster]}
|
|
<div class={"cluster-listing" + (clusterName === config.clusterName ? " selected" : "")}>
|
|
<h2 class="cluster-title">{cluster.clusterName}</h2>
|
|
<div class="cluster-buttons">
|
|
<button on:click={() => { config = deepCopy(clusters[clusterName]); }}>View & Edit</button>
|
|
<button on:click={() => deleteCluster(clusterName)}>Delete</button>
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
|
|
<style>
|
|
.cluster-listing {
|
|
display: flex;
|
|
flex-direction: row;
|
|
border-style: solid;
|
|
border-width: 1px;
|
|
padding: 1em;
|
|
margin: 1em;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.selected {
|
|
background-color: rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.cluster-buttons button {
|
|
min-width: 5em;
|
|
margin-left: 1em;
|
|
}
|
|
|
|
.cluster-title {
|
|
|
|
}
|
|
|
|
section {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex: 1;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
h1 {
|
|
width: 100%;
|
|
}
|
|
|
|
textarea {
|
|
overflow: hidden;
|
|
z-index: 1;
|
|
margin-left: 0;
|
|
|
|
background-color: #222222;
|
|
color: #d0dde9;
|
|
|
|
border: none;
|
|
font-size: 14px;
|
|
font-family: var(--font-mono);
|
|
padding: 1em;
|
|
overflow-x: auto;
|
|
min-width: 20vw;
|
|
}
|
|
|
|
.query-settings {
|
|
margin-right: 1rem;
|
|
padding: 1em;
|
|
height: 86vh;
|
|
overflow: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.settings-sub-option {
|
|
margin-top: 0.2em;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.no-query-data h2 {
|
|
font-size: 32px;
|
|
color: rgba(0, 0, 0, 0.3);
|
|
}
|
|
</style>
|