Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b1def98
Bump rollup from 2.79.1 to 2.79.2
dependabot[bot] Sep 27, 2024
42b4881
Add support for only-cached mode
kriszyp Oct 4, 2024
0e13eff
Add support for trying to log into servers and using cookies for main…
kriszyp Oct 7, 2024
5fa72b8
Remove system information that is no longer available (because it was…
kriszyp Oct 7, 2024
bb1e5b8
Remove system information that is no longer available (because it was…
kriszyp Oct 7, 2024
5a639c2
Fix handling of tables without a primary key attribute
kriszyp Oct 7, 2024
8977b88
Define a base config that can stay in sync without leaving all the va…
kriszyp Oct 7, 2024
d7b8538
styles
deliciousmonster Oct 7, 2024
7c8b7b2
Merge pull request #398 from HarperDB/remove_broken-sysinfo
deliciousmonster Oct 7, 2024
ebfa7bf
Merge branch 'stage' into remove-broken-sysinfo
deliciousmonster Oct 7, 2024
854cd41
Merge pull request #399 from HarperDB/remove-broken-sysinfo
deliciousmonster Oct 7, 2024
7d934bd
Merge branch 'stage' into only-cached-mode
deliciousmonster Oct 7, 2024
a85b364
Merge branch 'stage' into dependabot/npm_and_yarn/rollup-2.79.2
deliciousmonster Oct 7, 2024
14741ef
Merge pull request #395 from HarperDB/only-cached-mode
deliciousmonster Oct 7, 2024
abcbc1f
Merge branch 'stage' into dependabot/npm_and_yarn/rollup-2.79.2
deliciousmonster Oct 7, 2024
e3548f4
Merge pull request #394 from HarperDB/dependabot/npm_and_yarn/rollup-…
deliciousmonster Oct 7, 2024
9c66cf4
v4.5.8
deliciousmonster Oct 7, 2024
8b2ad63
package updates, linter errors
deliciousmonster Oct 7, 2024
cb35e6d
Only add metadata id for tables that are lacking a primary key
kriszyp Oct 7, 2024
9d71feb
Merge branch 'stage' into only-cached-mode-2
deliciousmonster Oct 7, 2024
0f666d6
Merge pull request #401 from HarperDB/only-cached-mode-2
deliciousmonster Oct 7, 2024
b7a9c3c
linter
deliciousmonster Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "harperdb-studio",
"version": "4.5.7",
"version": "4.5.8",
"description": "A UI for HarperDB",
"deploymentUrl": "studio.harperdb.io",
"private": true,
Expand Down
10 changes: 0 additions & 10 deletions src/assets/styles/pages/_config.scss
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
#local-studio {
#config {
.instance-details {
min-height: auto !important;
}
}
}

#config {
.instance-details {
min-height: 152px;

.card-body {
padding-bottom: 0;
}
Expand Down
14 changes: 12 additions & 2 deletions src/components/instance/browse/BrowseDatatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {
filtered: tableState.filtered,
pageSize: tableState.pageSize,
sorted: tableState.sorted,
onlyCached: tableState.onlyCached,
page: tableState.page,
auth,
url,
Expand All @@ -78,7 +79,13 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {

if (isMounted) {
setLoading(false);
if (!newData.error) {
if (newData.error) {
setTableState({
...tableState,
tableData: [],
error,
});
} else {
if (!tableState.filtered.length) {
setTotalPages(newTotalPages);
setTotalRecords(newTotalRecords);
Expand Down Expand Up @@ -109,7 +116,7 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {
isMounted = false;
};
// eslint-disable-next-line
}, [tableState.sorted, tableState.page, tableState.filtered, tableState.pageSize, lastUpdate, activeTable]);
}, [tableState.sorted, tableState.page, tableState.filtered, tableState.pageSize, tableState.onlyCached, lastUpdate, activeTable]);

useInterval(() => tableState.autoRefresh && setLastUpdate(Date.now()), config.refresh_content_interval);

Expand All @@ -120,8 +127,10 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {
loading={loading}
loadingFilter={loadingFilter}
autoRefresh={tableState.autoRefresh}
onlyCached={tableState.onlyCached}
refresh={() => setLastUpdate(Date.now())}
toggleAutoRefresh={() => setTableState({ ...tableState, autoRefresh: !tableState.autoRefresh })}
toggleOnlyCached={() => setTableState({ ...tableState, onlyCached: !tableState.onlyCached })}
toggleFilter={() => setTableState({ ...tableState, showFilter: !tableState.showFilter })}
/>
<Card className="my-3">
Expand All @@ -130,6 +139,7 @@ function BrowseDatatable({ tableState, setTableState, activeTable }) {
manual
columns={tableState.dataTableColumns || []}
data={tableState.tableData || []}
error={tableState.error}
currentPage={tableState.page}
pageSize={tableState.pageSize}
totalPages={totalPages || 0}
Expand Down
7 changes: 6 additions & 1 deletion src/components/instance/browse/BrowseDatatableHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom';

import commaNumbers from '../../../functions/util/commaNumbers';

function BrowseDatatableHeader({ totalRecords, loading, loadingFilter, refresh, autoRefresh, toggleAutoRefresh, toggleFilter }) {
function BrowseDatatableHeader({ totalRecords, loading, loadingFilter, refresh, autoRefresh, onlyCached, toggleAutoRefresh, toggleOnlyCached, toggleFilter }) {
const navigate = useNavigate();
const { compute_stack_id, schema, table, customer_id } = useParams();

Expand Down Expand Up @@ -32,6 +32,11 @@ function BrowseDatatableHeader({ totalRecords, loading, loadingFilter, refresh,
<i className={`fa fa-lg fa-toggle-${autoRefresh ? 'on' : 'off'}`} />
</Button>
<span className="mx-3 text">|</span>
<Button id="toggleOnlyCached" color="link" tabIndex="0" title="Only show cached data" onClick={toggleOnlyCached}>
<span className="me-2">only cached</span>
<i className={`fa fa-lg fa-toggle-${onlyCached ? 'on' : 'off'}`} />
</Button>
<span className="mx-3 text">|</span>
<Button id="toggleSearch" color="link" tabIndex="0" title={`Filter table ${table}`} className="me-3" onClick={toggleFilter}>
<i className="fa fa-search" />
</Button>
Expand Down
4 changes: 3 additions & 1 deletion src/components/instance/browse/JSONEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ function JSONEditor({ newEntityAttributes, hashAttribute }) {
schema,
table,
hash_values,
get_attributes: ['*']
get_attributes: ['*'],
onlyIfCached: true,
noCacheStore: true
},
auth,
url
Expand Down
1 change: 1 addition & 0 deletions src/components/instance/browse/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const defaultTableState = {
page: 0,
pageSize: 20,
autoRefresh: false,
onlyCached: false,
showFilter: false,
newEntityAttributes: false,
hashAttribute: false,
Expand Down
3 changes: 3 additions & 0 deletions src/components/instance/query/Datatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const defaultTableState = {
totalPages: 1,
pageSize: 20,
autoRefresh: false,
onlyCached: false,
showFilter: false,
lastUpdate: false,
};
Expand Down Expand Up @@ -103,7 +104,9 @@ function Datatable({ query }) {
filtered={tableState.filtered}
toggleFilter={(newValues) => setTableState({ ...tableState, ...newValues })}
autoRefresh={tableState.autoRefresh}
onlyCached={tableState.onlyCached}
setAutoRefresh={() => setTableState({ ...tableState, autoRefresh: !tableState.autoRefresh })}
setOnlyCached={() => setTableState({ ...tableState, onlyCached: !tableState.onlyCached })}
setLastUpdate={setLastUpdate}
setShowChartModal={setShowChartModal}
/>
Expand Down
58 changes: 6 additions & 52 deletions src/components/instance/status/SystemInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useStoreState } from 'pullstate';
import { Card, CardBody, Row, Col, Button } from 'reactstrap';
import { ErrorBoundary } from 'react-error-boundary';

import appState from '../../../functions/state/appState';
import instanceState from '../../../functions/state/instanceState';

import updateSystemInfo from '../../../functions/api/instance/updateSystemInfo';
Expand All @@ -14,14 +13,10 @@ import addError from '../../../functions/api/lms/addError';
let controller;

function SystemInfo() {
const compute_stack_id = useStoreState(instanceState, (s) => s.compute_stack_id);
const auth = useStoreState(instanceState, (s) => s.auth);
const url = useStoreState(instanceState, (s) => s.url);
const systemInfo = useStoreState(instanceState, (s) => s.systemInfo);
const is_local = useStoreState(instanceState, (s) => s.is_local);
const storage = useStoreState(instanceState, (s) => s.storage);
const iopsAlarms = useStoreState(appState, (s) => s.alarms && s.alarms[compute_stack_id]?.alarmCounts['Disk I/O'], [compute_stack_id]);
const [autoRefresh, setAutoRefresh] = useState(false);
const [loading, setLoading] = useState(!systemInfo?.totalMemory);

const fetchData = async () => {
Expand All @@ -48,82 +43,41 @@ function SystemInfo() {
<Button color="link" title="Update Metrics" className="me-2" onClick={fetchData}>
<i className={`fa ${loading ? 'fa-spinner fa-spin' : 'fa-sync-alt'}`} />
</Button>
<Button color="link" title="Turn on autofresh" onClick={() => setAutoRefresh(!autoRefresh)}>
<span className="me-2">auto</span>
<i className={`fa fa-lg fa-toggle-${autoRefresh ? 'on' : 'off'}`} />
</Button>
</Col>
</Row>
<Card className="my-3 instance-details">
<CardBody>
{!systemInfo && !autoRefresh ? (
{!systemInfo ? (
<div className="pt-5 text-center">
<i className="fa fa-spinner fa-spin text-purple" />
</div>
) : (
<Row>
<Col md="2" sm="4" xs="6">
<Col>
<ContentContainer header="Total Memory" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.totalMemory || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<Col>
<ContentContainer header="Active Memory" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.usedMemory || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<Col>
<ContentContainer header="Available Memory" className="mb-3">
<div className={`nowrap-scroll text-${systemInfo?.memoryStatus || 'grey'}`}>{systemInfo?.freeMemory || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Total Disk" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.totalDisk || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Used Disk" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.usedDisk || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Free Disk" className="mb-3">
<div className={`nowrap-scroll text-${systemInfo?.diskStatus || 'grey'}`}>{systemInfo?.freeDisk || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<Col>
<ContentContainer header="CPU Cores" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.cpuCores || '...'}</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<Col>
<ContentContainer header="CPU Load" className="mb-3">
<div className={`nowrap-scroll text-${systemInfo?.cpuStatus || 'grey'}`}>{systemInfo?.cpuLoad || '...'}%</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Network Volume Up" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.networkTransferred || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Network Volume Down" className="mb-3">
<div className="nowrap-scroll">{systemInfo?.networkReceived || '...'}GB</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Network Latency" className="mb-3">
<div className={`nowrap-scroll text-${systemInfo?.networkLatencyStatus || 'grey'}`}>
{systemInfo?.networkLatency ? `${systemInfo?.networkLatency} ms` : 'N/A'}
</div>
</ContentContainer>
</Col>
<Col md="2" sm="4" xs="6">
<ContentContainer header="Disk IOPS" className="mb-3">
<div className={`nowrap-scroll text-${iopsAlarms ? 'danger' : ''}`}>{is_local ? 'HARDWARE LIMIT' : storage?.iops || '...'}</div>
</ContentContainer>
</Col>
</Row>
)}
</CardBody>
Expand Down
19 changes: 17 additions & 2 deletions src/components/instances/list/CardBackLogin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ErrorBoundary } from 'react-error-boundary';

import useInstanceAuth from '../../../functions/state/instanceAuths';
import handleCloudInstanceUsernameChange from '../../../functions/instances/handleCloudInstanceUsernameChange';
import login from '../../../functions/api/instance/login';
import userInfo from '../../../functions/api/instance/userInfo';
import ErrorFallback from '../../shared/ErrorFallback';
import addError from '../../../functions/api/lms/addError';
Expand All @@ -22,7 +23,21 @@ function CardBackLogin({ compute_stack_id, url, is_ssl, setFlipState, flipState,
if (!user || !pass) {
setFormState({ error: 'All fields are required' });
} else {
const result = await userInfo({ auth: { user, pass }, url });
let login_not_supported;
let result;
if (pass) {
// try to log in with the provided credentials
const login_result = await login({ auth: { user, pass }, url });
if (login_result.error) {
login_not_supported = true;
} else {
result = await userInfo({ url });
if (result.error)
login_not_supported = true;
}
if (login_not_supported) result = await userInfo({ auth: { user, pass }, url });
} else
result = await userInfo({ url });

if (is_ssl && is_local && result.error && result.type === 'catch') {
setFormState({ error: 'SSL ERROR. ACCEPT SELF-SIGNED CERT?', url });
Expand All @@ -46,7 +61,7 @@ function CardBackLogin({ compute_stack_id, url, is_ssl, setFlipState, flipState,
} else {
setInstanceAuths({
...instanceAuths,
[compute_stack_id]: { user: formData.user, pass: formData.pass, super: result.role.permission.super_user, structure: result.role.permission.structure_user },
[compute_stack_id]: { user: formData.user, pass: login_not_supported ? formData.pass : '', super: result.role.permission.super_user, structure: result.role.permission.structure_user },
});
setTimeout(() => setFlipState(false), 100);
}
Expand Down
5 changes: 3 additions & 2 deletions src/components/shared/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const defaultColumn = {
function DataTable({
columns,
data,
error,
currentPage,
pageSize,
totalPages,
Expand Down Expand Up @@ -108,7 +109,7 @@ function DataTable({
}, [iterable?.length]);

return (
<ErrorBoundary onError={(error, componentStack) => addError({ error: { message: error.message, componentStack } })} FallbackComponent={ErrorFallback}>
<ErrorBoundary onError={(err, componentStack) => addError({ error: { message: err.message, componentStack } })} FallbackComponent={ErrorFallback}>
<div className="react-table-scroller">
<DataTableHeader headerGroups={headerGroups} onSortedChange={onSortedChange} sorted={sorted} showFilter={showFilter} />
{loading || localLoading ? (
Expand All @@ -120,7 +121,7 @@ function DataTable({
) : (
<div className="centered text-center">
<i className="fa fa-exclamation-triangle text-danger" />
<div className="mt-2 text-darkgrey">no records</div>
<div className="mt-2 text-darkgrey">{error ? `Error loading data: ${error}` : 'no records'}</div>
</div>
)}
</div>
Expand Down
17 changes: 17 additions & 0 deletions src/config/index.base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export default {
studio_version: 'x.x.xx',
env: 'dev',
stripe_public_key: 'pk_test_XXXXXXXXXXXXXXXXXXXXXX',
lms_api_url: 'https://dev.harperdbcloudservices.com/',
google_analytics_code: 'UA-XXXXXXXXXXXXX-3',
tc_version: '2020-01-01',
check_version_interval: 300000,
check_user_interval: 900000,
refresh_content_interval: 10000,
free_cloud_instance_limit: 1,
max_file_upload_size: 10380902,
alarm_badge_threshold: 86400,
maintenance: 0,
is_local_studio: process.env.REACT_APP_LOCALSTUDIO, // this is injected at build-time and loads LocalApp.js instead of App.js
local_studio_dev_url: 'http://localhost:9925', // this lets you dev the UI on port 3000 and talk to your local instance on 9925
};
12 changes: 0 additions & 12 deletions src/config/index.example.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
export default {
studio_version: 'x.x.xx',
env: 'dev',
stripe_public_key: 'pk_test_XXXXXXXXXXXXXXXXXXXXXX',
lms_api_url: 'XXXXXXXXXXXXXXXXXXXXXXXX',
google_analytics_code: 'UA-XXXXXXXXXXXXX-3',
tc_version: '2020-01-01',
check_version_interval: 300000,
check_user_interval: 900000,
refresh_content_interval: 10000,
free_cloud_instance_limit: 1,
max_file_upload_size: 10380902,
alarm_badge_threshold: 86400,
maintenance: 0,
is_local_studio: process.env.REACT_APP_LOCALSTUDIO, // this is injected at build-time and loads LocalApp.js instead of App.js
local_studio_dev_url: 'http://localhost:9925', // this lets you dev the UI on port 3000 and talk to your local instance on 9925
};
9 changes: 9 additions & 0 deletions src/functions/api/instance/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import queryInstance from '../queryInstance';

export default async ({ auth, url }) =>
queryInstance({
operation: { operation: 'login', username: auth.user, password: auth.pass },
auth,
url,
timeout: 5000,
});
4 changes: 2 additions & 2 deletions src/functions/api/instance/searchByConditions.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import queryInstance from '../queryInstance';

export default async ({ auth, url, schema, table, operator, get_attributes, limit, offset, conditions, signal }) =>
export default async ({ auth, url, schema, table, operator, get_attributes, limit, offset, onlyCached, conditions, signal }) =>
queryInstance({
operation: { operation: 'search_by_conditions', schema, table, operator, get_attributes, limit, offset, conditions },
operation: { operation: 'search_by_conditions', schema, table, operator, get_attributes, limit, offset, conditions, onlyIfCached: onlyCached, noCacheStore: onlyCached },
auth,
url,
signal,
Expand Down
4 changes: 2 additions & 2 deletions src/functions/api/instance/searchByValue.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import queryInstance from '../queryInstance';

export default async ({ auth, url, schema, table, search_attribute, search_value, get_attributes, limit, offset, signal }) =>
export default async ({ auth, url, schema, table, search_attribute, search_value, get_attributes, limit, offset, sort, onlyCached, signal }) =>
queryInstance({
operation: { operation: 'search_by_value', schema, table, search_attribute, search_value, get_attributes, limit, offset },
operation: { operation: 'search_by_value', schema, table, search_attribute, search_value, get_attributes, limit, offset, sort, onlyIfCached: onlyCached, noCacheStore: onlyCached },
auth,
url,
signal,
Expand Down
Loading