Skip to content

Commit

Permalink
Merge pull request #85 from performant-software/feature/iiif82_system…
Browse files Browse the repository at this point in the history
…_status

IIIF #82 - System status
  • Loading branch information
dleadbetter authored Jan 16, 2025
2 parents 6fd0a90 + 9390ea4 commit 8f85a40
Show file tree
Hide file tree
Showing 23 changed files with 847 additions and 63 deletions.
32 changes: 32 additions & 0 deletions app/controllers/api/dashboard_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class Api::DashboardController < Api::BaseController
def heap
render json: { errors: [I18n.t('errors.dashboard_controller.unauthorized')] }, status: :unauthorized and return unless current_user.admin?

service = Iiif::Server.new

json = serializer.heap(
service.status.dig(:data)
)

render json: json, status: :ok
end

def status
render json: { errors: [I18n.t('errors.dashboard_controller.unauthorized')] }, status: :unauthorized and return unless current_user.admin?

service = Iiif::Server.new

json = serializer.status(
service.health.dig(:data, :color),
service.status.dig(:data)
)

render json: json, status: :ok
end

private

def serializer
DashboardSerializer.new
end
end
7 changes: 3 additions & 4 deletions app/controllers/api/resources_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ def clear_cache
resource = Resource.find(params[:id])
key = resource.send(params[:attribute])&.key

service = Cantaloupe::Api.new
service = Iiif::Server.new
response = service.clear_cache(key)

if response.success?
if response[:success?]
render json: {}, status: :ok
else
error = response['exception'] || response['message'] || response['errors']
render json: { errors: [error] }, status: :bad_request
render json: { errors: [response[:errors]] }, status: :bad_request
end
end

Expand Down
26 changes: 26 additions & 0 deletions app/serializers/dashboard_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class DashboardSerializer
def heap(info)
{
used: info.dig(:vm, :usedHeapBytes),
max: info.dig(:vm, :maxHeapBytes)
}
end

def status(color, info)
{
application: {
name: I18n.t('dashboard_serializer.application_name'),
version: info.dig(:application, :version)
},
vm: {
name: info.dig(:vm, :name),
version: info.dig(:vm, :version)
},
os: {
name: info.dig(:vm, :vendor),
processors: info.dig(:vm, :numProcessors)
},
status_color: color
}
end
end
19 changes: 0 additions & 19 deletions app/services/cantaloupe/api.rb

This file was deleted.

45 changes: 45 additions & 0 deletions app/services/iiif/server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Iiif
class Server

def clear_cache(key)
body = {
verb: 'PurgeItemFromCache',
identifier: key
}

parse_response HTTParty.post("#{ENV['IIIF_HOST']}/tasks", body: body.to_json, basic_auth: basic_auth)
end

def health
parse_response HTTParty.get("#{ENV['IIIF_HOST']}/health")
end

def status
parse_response HTTParty.get("#{ENV['IIIF_HOST']}/status", basic_auth: basic_auth)
end

private

def basic_auth
{
username: ENV['CANTALOUPE_API_USERNAME'],
password: ENV['CANTALOUPE_API_PASSWORD']
}
end

def parse_response(response)
if response.body.nil? || response.body.empty?
body = '{}'
else
body = response.body
end

{
success?: response.success?,
data: JSON.parse(body, symbolize_names: true),
errors: response['exception'] || response['message'] || response['errors']
}
end

end
end
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-uuid": "^1.0.2",
"recharts": "^2.15.0",
"semantic-ui-react": "^2.1.2",
"underscore": "^1.13.3"
},
Expand Down
31 changes: 0 additions & 31 deletions client/src/components/AttachmentStatus.js

This file was deleted.

28 changes: 28 additions & 0 deletions client/src/components/AttributeView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @flow

import React, { type ComponentType } from 'react';
import styles from './AttributeView.module.css';

type Props = {
label: string,
value: string
};

const AttributeView: ComponentType<any> = (props: Props) => (
<div
className={styles.gridItem}
>
<div
className={styles.label}
>
{ props.label }
</div>
<div
className={styles.value}
>
{ props.value }
</div>
</div>
);

export default AttributeView;
8 changes: 8 additions & 0 deletions client/src/components/AttributeView.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.gridItem .label {
color: rgba(0, 0, 0, .6);
padding-top: 0.25em;
}

.gridItem .value {
font-weight: bold;
}
45 changes: 45 additions & 0 deletions client/src/components/StatusIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @flow

import React, { type ComponentType } from 'react';
import { Icon } from 'semantic-ui-react';

type Props = {
className?: string,
status: 'positive' | 'warning' | 'negative'
};

const StatusIcon: ComponentType<any> = (props: Props) => {
if (props.status === 'positive') {
return (
<Icon
className={props.className}
color='green'
name='check circle'
/>
);
}

if (props.status === 'warning') {
return (
<Icon
className={props.className}
color='yellow'
name='warning circle'
/>
);
}

if (props.status === 'negative') {
return (
<Icon
className={props.className}
color='red'
name='times circle'
/>
);
}

return null;
};

export default StatusIcon;
52 changes: 52 additions & 0 deletions client/src/components/Widget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// @flow

import cx from 'classnames';
import React, { type ComponentType, type Node } from 'react';
import {
Button,
Dimmer,
Header,
Loader
} from 'semantic-ui-react';
import styles from './Widget.module.css';

type Props = {
children: Node,
className?: string,
loading: boolean,
onReload?: () => void,
title: string
};

const Widget: ComponentType<any> = (props: Props) => (
<div
className={cx(styles.widget, props.className)}
>
<Dimmer
active={props.loading}
inverted
>
<Loader />
</Dimmer>
<div
className={styles.headerContainer}
>
<Header
className={cx(styles.ui, styles.header)}
>
{ props.title }
</Header>
{ props.onReload && (
<Button
basic
className={cx(styles.ui, styles.button, styles.reload)}
icon='refresh'
onClick={props.onReload}
/>
)}
</div>
{ props.children }
</div>
);

export default Widget;
16 changes: 16 additions & 0 deletions client/src/components/Widget.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.widget > .headerContainer {
display: flex;
justify-content: space-between;
}

.widget > .headerContainer > .ui.header {
margin-top: 0;
}

.widget > .headerContainer > .ui.button.reload,
.widget > .headerContainer > .ui.button.reload:active,
.widget > .headerContainer > .ui.button.reload:hover,
.widget > .headerContainer > .ui.button.reload:focus {
background: none !important;
box-shadow: none !important;
}
18 changes: 18 additions & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@
"name": "Name"
}
},
"HeapSpace": {
"labels": {
"free": "Free",
"used": "Used"
},
"title": "Heap Space"
},
"Home": {
"header": "Welcome to Rails-React Template <1>Edit <1>src/App.js</1> and save to reload.</1>"
},
Expand Down Expand Up @@ -178,6 +185,17 @@
"header": "Password Policy"
}
},
"SystemStatus": {
"labels": {
"application": "Application",
"operatingSystem": "Operating system",
"processors": "Processors",
"version": "Version",
"vm": "VM",
"vmVersion": "VM version"
},
"title": "System Status"
},
"UserModal": {
"labels": {
"user": "User"
Expand Down
Loading

0 comments on commit 8f85a40

Please sign in to comment.