mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-24 19:56:00 +00:00
redis: set ttl
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: console.log(title, indentLevel);
|
||||
// $: console.log(title, indentLevel);
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
||||
@@ -1,41 +1,92 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { onMount, tick } from 'svelte';
|
||||
|
||||
import ScrollableTableControl from '../elements/ScrollableTableControl.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import createRef from '../utility/createRef';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
export let keyInfo;
|
||||
export let onChangeSelected;
|
||||
|
||||
let rows = [];
|
||||
let cursor = 0;
|
||||
let isLoading = false;
|
||||
let loadNextNeeded = false;
|
||||
let isLoadedAll = false;
|
||||
let selectedIndex;
|
||||
const oldIndexRef = createRef(null);
|
||||
|
||||
async function loadRows() {
|
||||
const res = await apiCall('database-connections/load-key-table-range', {
|
||||
conid,
|
||||
database,
|
||||
key: keyInfo.key,
|
||||
cursor,
|
||||
count: 10,
|
||||
});
|
||||
rows = res.items;
|
||||
async function loadNextRows() {
|
||||
if (isLoading) {
|
||||
// console.log('ALREADY LOADING');
|
||||
loadNextNeeded = true;
|
||||
return;
|
||||
}
|
||||
isLoading = true;
|
||||
try {
|
||||
const res = await apiCall('database-connections/load-key-table-range', {
|
||||
conid,
|
||||
database,
|
||||
key: keyInfo.key,
|
||||
cursor,
|
||||
count: 10,
|
||||
});
|
||||
const newRows = [...rows];
|
||||
for (const row of res.items) {
|
||||
if (keyInfo.keyColumn && newRows.find(x => x[keyInfo.keyColumn] == row[keyInfo.keyColumn])) {
|
||||
continue;
|
||||
}
|
||||
newRows.push({ rowNumber: newRows.length + 1, ...row });
|
||||
}
|
||||
|
||||
rows = newRows;
|
||||
cursor = res.cursor;
|
||||
isLoadedAll = cursor == 0;
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
if (loadNextNeeded) {
|
||||
loadNextNeeded = false;
|
||||
await tick();
|
||||
loadNextRows();
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if (onChangeSelected && rows[selectedIndex]) {
|
||||
if (oldIndexRef.get() != selectedIndex) {
|
||||
oldIndexRef.set(selectedIndex);
|
||||
onChangeSelected(rows[selectedIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
keyInfo;
|
||||
}
|
||||
onMount(() => {
|
||||
loadRows();
|
||||
loadNextRows();
|
||||
});
|
||||
</script>
|
||||
|
||||
<ScrollableTableControl
|
||||
columns={[
|
||||
keyInfo.tableColumns.map(fieldName => ({
|
||||
fieldName,
|
||||
header: fieldName,
|
||||
{
|
||||
fieldName: 'rowNumber',
|
||||
header: 'num',
|
||||
width: '60px',
|
||||
},
|
||||
...keyInfo.tableColumns.map(column => ({
|
||||
fieldName: column.name,
|
||||
header: column.name,
|
||||
})),
|
||||
]}
|
||||
{rows}
|
||||
onLoadNext={isLoadedAll ? null : loadNextRows}
|
||||
selectable
|
||||
singleLineRow
|
||||
bind:selectedIndex
|
||||
/>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import resizeObserver from '../utility/resizeObserver';
|
||||
@@ -27,6 +27,8 @@
|
||||
export let selectedIndex = 0;
|
||||
export let clickable = false;
|
||||
export let disableFocusOutline = false;
|
||||
export let onLoadNext = null;
|
||||
export let singleLineRow = false;
|
||||
|
||||
export let domTable = undefined;
|
||||
|
||||
@@ -34,6 +36,36 @@
|
||||
let headerHeight = 0;
|
||||
let domBody;
|
||||
|
||||
let domLoadNext;
|
||||
let observer;
|
||||
|
||||
function startObserver(dom) {
|
||||
if (observer) {
|
||||
// console.log('STOP OBSERVE');
|
||||
observer.disconnect();
|
||||
observer = null;
|
||||
}
|
||||
if (dom) {
|
||||
// console.log('OBSERVE');
|
||||
observer = new IntersectionObserver(entries => {
|
||||
// console.log('ENTRIES', entries);
|
||||
if (entries.find(x => x.isIntersecting)) {
|
||||
// console.log('INVOKE LOAD NEXT');
|
||||
onLoadNext();
|
||||
}
|
||||
});
|
||||
observer.observe(dom);
|
||||
}
|
||||
}
|
||||
|
||||
$: startObserver(domLoadNext);
|
||||
|
||||
onDestroy(() => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: columnList = _.compact(_.flatten(columns));
|
||||
@@ -86,6 +118,7 @@
|
||||
bind:this={domTable}
|
||||
class:selectable
|
||||
class:disableFocusOutline
|
||||
class:singleLineRow
|
||||
on:keydown
|
||||
tabindex={selectable ? -1 : undefined}
|
||||
on:keydown={handleKeyDown}
|
||||
@@ -163,6 +196,13 @@
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
{#if onLoadNext}
|
||||
{#key rows}
|
||||
<tr>
|
||||
<td colspan={columnList.length} bind:this={domLoadNext}> Loading next rows... </td>
|
||||
</tr>
|
||||
{/key}
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -193,6 +233,11 @@
|
||||
table tbody tr td {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table.singleLineRow tbody tr td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table tbody {
|
||||
display: block;
|
||||
overflow-y: scroll;
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
import { getIconForRedisType } from 'dbgate-tools';
|
||||
import TextField from '../forms/TextField.svelte';
|
||||
import DbKeyTableControl from '../datagrid/DbKeyTableControl.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import _ from 'lodash';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -27,8 +30,43 @@
|
||||
|
||||
export const activator = createActivator('DbKeyDetailTab', true);
|
||||
|
||||
let currentRow;
|
||||
|
||||
$: key = $activeDbKeysStore[`${conid}:${database}`];
|
||||
let refreshToken = 0;
|
||||
|
||||
function handleChangeTtl(keyInfo) {
|
||||
showModal(InputTextModal, {
|
||||
value: keyInfo.ttl,
|
||||
label: 'New TTL value (-1=key never expires)',
|
||||
header: `Set TTL for key ${keyInfo.key}`,
|
||||
onConfirm: async value => {
|
||||
const ttl = parseInt(value);
|
||||
if (_.isNumber(ttl)) {
|
||||
if (ttl < 0) {
|
||||
await apiCall('database-connections/call-method', {
|
||||
conid,
|
||||
database,
|
||||
method: 'persist',
|
||||
args: [keyInfo.key],
|
||||
});
|
||||
} else {
|
||||
await apiCall('database-connections/call-method', {
|
||||
conid,
|
||||
database,
|
||||
method: 'expire',
|
||||
args: [keyInfo.key, ttl],
|
||||
});
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
refreshToken += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await apiCall('database-connections/load-key-info', { conid, database, key, refreshToken })}
|
||||
@@ -36,28 +74,38 @@
|
||||
{:then keyInfo}
|
||||
<div class="container">
|
||||
<div class="top-panel">
|
||||
<FontIcon icon={getIconForRedisType(keyInfo.type)} padRight />
|
||||
<div class="type">
|
||||
<FontIcon icon={getIconForRedisType(keyInfo.type)} padRight />
|
||||
{keyInfo.type}
|
||||
</div>
|
||||
<TextField value={key} readOnly />
|
||||
{key}
|
||||
TTL:{keyInfo.ttl}
|
||||
<FormStyledButton
|
||||
value="Refresh"
|
||||
on:click={() => {
|
||||
refreshToken += 1;
|
||||
}}
|
||||
/>
|
||||
<div class="key-name">
|
||||
<TextField value={key} readOnly />
|
||||
</div>
|
||||
<FormStyledButton value={`TTL:${keyInfo.ttl}`} on:click={() => handleChangeTtl(keyInfo)} />
|
||||
<FormStyledButton value="Refresh" on:click={refresh} />
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{#if keyInfo.tableColumns}
|
||||
<VerticalSplitter>
|
||||
<svelte:fragment slot="1">
|
||||
<DbKeyTableControl {conid} {database} {keyInfo} />
|
||||
<DbKeyTableControl
|
||||
{conid}
|
||||
{database}
|
||||
{keyInfo}
|
||||
onChangeSelected={row => {
|
||||
currentRow = row;
|
||||
}}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="2">PROPS</svelte:fragment>
|
||||
<div slot="2" class="props">
|
||||
{#each keyInfo.tableColumns as column}
|
||||
<div class="colname">{column.name}</div>
|
||||
<div class="colvalue">
|
||||
<AceEditor readOnly value={currentRow && currentRow[column.name]} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</VerticalSplitter>
|
||||
{:else}
|
||||
<AceEditor readOnly value={keyInfo.value} />
|
||||
@@ -80,9 +128,36 @@
|
||||
|
||||
.top-panel {
|
||||
display: flex;
|
||||
background: var(--theme-bg-2);
|
||||
}
|
||||
|
||||
.type {
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.props {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.colname {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.colvalue {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.key-name {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.key-name :global(input) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user