mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[PUI] Added Server Info Modal (#5810)
* updated typing to allow either link or action * fixed typing * made it possible to use an action instead of a link * added ServerInfo Modal skeleton * fixed anchor * added content to ServerInfo * Factored database lookup out * Extended status API to CUI level * extended ServerInfo to CUI level * Made modal larger * fixed default settings
This commit is contained in:
		| @@ -18,10 +18,11 @@ from InvenTree.permissions import RolePermission | ||||
| from part.templatetags.inventree_extras import plugins_info | ||||
| from plugin.serializers import MetadataSerializer | ||||
|  | ||||
| from .email import is_email_configured | ||||
| from .mixins import RetrieveUpdateAPI | ||||
| from .status import is_worker_running | ||||
| from .version import (inventreeApiVersion, inventreeInstanceName, | ||||
|                       inventreeVersion) | ||||
| from .status import check_system_health, is_worker_running | ||||
| from .version import (inventreeApiVersion, inventreeDatabase, | ||||
|                       inventreeInstanceName, inventreeVersion) | ||||
| from .views import AjaxView | ||||
|  | ||||
|  | ||||
| @@ -48,6 +49,11 @@ class InfoView(AjaxView): | ||||
|             'worker_pending_tasks': self.worker_pending_tasks(), | ||||
|             'plugins_enabled': settings.PLUGINS_ENABLED, | ||||
|             'active_plugins': plugins_info(), | ||||
|             'email_configured': is_email_configured(), | ||||
|             'debug_mode': settings.DEBUG, | ||||
|             'docker_mode': settings.DOCKER, | ||||
|             'system_health': check_system_health() if request.user.is_staff else None, | ||||
|             'database': inventreeDatabase()if request.user.is_staff else None | ||||
|         } | ||||
|  | ||||
|         return JsonResponse(data) | ||||
|   | ||||
| @@ -2,11 +2,14 @@ | ||||
|  | ||||
|  | ||||
| # InvenTree API version | ||||
| INVENTREE_API_VERSION = 142 | ||||
| INVENTREE_API_VERSION = 143 | ||||
|  | ||||
| """ | ||||
| Increment this API version number whenever there is a significant change to the API that any clients need to know about | ||||
|  | ||||
| v143 -> 2023-10-29: https://github.com/inventree/InvenTree/pull/5810 | ||||
|     - Extends the status endpoint to include information about system status and health | ||||
|  | ||||
| v142 -> 2023-10-20: https://github.com/inventree/InvenTree/pull/5759 | ||||
|     - Adds generic API endpoints for looking up status models | ||||
|  | ||||
|   | ||||
| @@ -199,3 +199,9 @@ def inventreeTarget(): | ||||
| def inventreePlatform(): | ||||
|     """Returns the platform for the instance.""" | ||||
|     return platform.platform(aliased=True) | ||||
|  | ||||
|  | ||||
| def inventreeDatabase(): | ||||
|     """Return the InvenTree database backend e.g. 'postgresql'.""" | ||||
|     db = settings.DATABASES['default'] | ||||
|     return db.get('ENGINE', None).replace('django.db.backends.', '') | ||||
|   | ||||
| @@ -183,13 +183,7 @@ def plugins_info(*args, **kwargs): | ||||
| @register.simple_tag() | ||||
| def inventree_db_engine(*args, **kwargs): | ||||
|     """Return the InvenTree database backend e.g. 'postgresql'.""" | ||||
|     db = djangosettings.DATABASES['default'] | ||||
|  | ||||
|     engine = db.get('ENGINE', _('Unknown database')) | ||||
|  | ||||
|     engine = engine.replace('django.db.backends.', '') | ||||
|  | ||||
|     return engine | ||||
|     return version.inventreeDatabase() or _('Unknown database') | ||||
|  | ||||
|  | ||||
| @register.simple_tag() | ||||
|   | ||||
| @@ -3,37 +3,81 @@ import { Anchor, Group, SimpleGrid, Text } from '@mantine/core'; | ||||
| import { DocTooltip } from './DocTooltip'; | ||||
| import { PlaceholderPill } from './Placeholder'; | ||||
|  | ||||
| export interface DocumentationLinkItem { | ||||
| interface DocumentationLinkBase { | ||||
|   id: string; | ||||
|   title: string | JSX.Element; | ||||
|   description: string | JSX.Element; | ||||
|   link: string; | ||||
|   placeholder?: boolean; | ||||
| } | ||||
|  | ||||
| interface DocumentationLinkItemLink extends DocumentationLinkBase { | ||||
|   link: string; | ||||
|   action?: never; | ||||
| } | ||||
|  | ||||
| interface DocumentationLinkItemAction extends DocumentationLinkBase { | ||||
|   link?: never; | ||||
|   action: () => void; | ||||
| } | ||||
|  | ||||
| export type DocumentationLinkItem = | ||||
|   | DocumentationLinkItemLink | ||||
|   | DocumentationLinkItemAction; | ||||
|  | ||||
| export function DocumentationLinks({ | ||||
|   links | ||||
| }: { | ||||
|   links: DocumentationLinkItem[]; | ||||
| }) { | ||||
|   const DocumentationLinkRenderer = ({ | ||||
|     link | ||||
|   }: { | ||||
|     link: DocumentationLinkItem; | ||||
|   }) => { | ||||
|     const content = ( | ||||
|       <Text size="sm" fw={500}> | ||||
|         {link.title} | ||||
|       </Text> | ||||
|     ); | ||||
|  | ||||
|     const Linker = ({ children }: { children: any }) => { | ||||
|       if (link.link) | ||||
|         return ( | ||||
|           <Anchor href={link.link} key={link.id}> | ||||
|             {children} | ||||
|           </Anchor> | ||||
|         ); | ||||
|  | ||||
|       if (link.action) | ||||
|         return ( | ||||
|           <Anchor component="button" type="button" onClick={link.action}> | ||||
|             {children} | ||||
|           </Anchor> | ||||
|         ); | ||||
|  | ||||
|       console.log('Neither link nor action found for link:', link); | ||||
|       return children; | ||||
|     }; | ||||
|  | ||||
|     return ( | ||||
|       <Linker> | ||||
|         {link.placeholder ? ( | ||||
|           <Group> | ||||
|             {content} | ||||
|             <PlaceholderPill /> | ||||
|           </Group> | ||||
|         ) : ( | ||||
|           content | ||||
|         )} | ||||
|       </Linker> | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|   return ( | ||||
|     <SimpleGrid cols={2} spacing={0}> | ||||
|       {links.map((link) => ( | ||||
|         <DocTooltip key={link.id} text={link.description}> | ||||
|           <Anchor href={link.link} key={link.id}> | ||||
|             {link.placeholder ? ( | ||||
|               <Group> | ||||
|                 <Text size="sm" fw={500}> | ||||
|                   {link.title} | ||||
|                 </Text> | ||||
|                 <PlaceholderPill /> | ||||
|               </Group> | ||||
|             ) : ( | ||||
|               <Text size="sm" fw={500}> | ||||
|                 {link.title} | ||||
|               </Text> | ||||
|             )} | ||||
|           </Anchor> | ||||
|           <DocumentationLinkRenderer link={link} /> | ||||
|         </DocTooltip> | ||||
|       ))} | ||||
|     </SimpleGrid> | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/frontend/src/components/items/OnlyStaff.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/frontend/src/components/items/OnlyStaff.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
|  | ||||
| import { useUserState } from '../../states/UserState'; | ||||
|  | ||||
| export const OnlyStaff = ({ children }: { children: any }) => { | ||||
|   const [user] = useUserState((state) => [state.user]); | ||||
|  | ||||
|   if (user?.is_staff) return children; | ||||
|   return <Trans>This information is only available for staff users</Trans>; | ||||
| }; | ||||
							
								
								
									
										141
									
								
								src/frontend/src/components/modals/ServerInfoModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/frontend/src/components/modals/ServerInfoModal.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { Badge, Button, Stack, Table, Title } from '@mantine/core'; | ||||
| import { ContextModalProps } from '@mantine/modals'; | ||||
|  | ||||
| import { useServerApiState } from '../../states/ApiState'; | ||||
| import { OnlyStaff } from '../items/OnlyStaff'; | ||||
|  | ||||
| export function ServerInfoModal({ | ||||
|   context, | ||||
|   id | ||||
| }: ContextModalProps<{ modalBody: string }>) { | ||||
|   const [server] = useServerApiState((state) => [state.server]); | ||||
|  | ||||
|   return ( | ||||
|     <Stack> | ||||
|       <Title order={5}> | ||||
|         <Trans>Server</Trans> | ||||
|       </Title> | ||||
|       <Table> | ||||
|         <tbody> | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>Instance Name</Trans> | ||||
|             </td> | ||||
|             <td>{server.instance}</td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>Database</Trans> | ||||
|             </td> | ||||
|             <td> | ||||
|               <OnlyStaff>{server.database}</OnlyStaff> | ||||
|             </td> | ||||
|           </tr> | ||||
|           {server.debug_mode && ( | ||||
|             <tr> | ||||
|               <td> | ||||
|                 <Trans>Bebug Mode</Trans> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <Trans>Server is running in debug mode</Trans> | ||||
|               </td> | ||||
|             </tr> | ||||
|           )} | ||||
|           {server.docker_mode && ( | ||||
|             <tr> | ||||
|               <td> | ||||
|                 <Trans>Docker Mode</Trans> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <Trans>Server is deployed using docker</Trans> | ||||
|               </td> | ||||
|             </tr> | ||||
|           )} | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>Plugin Support</Trans> | ||||
|             </td> | ||||
|             <td> | ||||
|               <Badge color={server.plugins_enabled ? 'green' : 'red'}> | ||||
|                 {server.plugins_enabled ? ( | ||||
|                   <Trans>Plugin support enabled</Trans> | ||||
|                 ) : ( | ||||
|                   <Trans>Plugin support disabled</Trans> | ||||
|                 )} | ||||
|               </Badge> | ||||
|             </td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>Server status</Trans> | ||||
|             </td> | ||||
|             <td> | ||||
|               <OnlyStaff> | ||||
|                 <Badge color={server.system_health ? 'green' : 'yellow'}> | ||||
|                   {server.system_health ? ( | ||||
|                     <Trans>Healthy</Trans> | ||||
|                   ) : ( | ||||
|                     <Trans>Issues detected</Trans> | ||||
|                   )} | ||||
|                 </Badge> | ||||
|               </OnlyStaff> | ||||
|             </td> | ||||
|           </tr> | ||||
|           {server.worker_running != true && ( | ||||
|             <tr> | ||||
|               <td> | ||||
|                 <Trans>Background Worker</Trans> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <Badge color="red"> | ||||
|                   <Trans>Background worker not running</Trans> | ||||
|                 </Badge> | ||||
|               </td> | ||||
|             </tr> | ||||
|           )} | ||||
|           {server.email_configured != true && ( | ||||
|             <tr> | ||||
|               <td> | ||||
|                 <Trans>Email Settings</Trans> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <Badge color="red"> | ||||
|                   <Trans>Email settings not configured</Trans> | ||||
|                 </Badge> | ||||
|               </td> | ||||
|             </tr> | ||||
|           )} | ||||
|         </tbody> | ||||
|       </Table> | ||||
|       <Title order={5}> | ||||
|         <Trans>Version</Trans> | ||||
|       </Title> | ||||
|       <Table> | ||||
|         <tbody> | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>Server Version</Trans> | ||||
|             </td> | ||||
|             <td>{server.version}</td> | ||||
|           </tr> | ||||
|           <tr> | ||||
|             <td> | ||||
|               <Trans>API Version</Trans> | ||||
|             </td> | ||||
|             <td>{server.apiVersion}</td> | ||||
|           </tr> | ||||
|         </tbody> | ||||
|       </Table> | ||||
|       <Button | ||||
|         color="red" | ||||
|         variant="outline" | ||||
|         onClick={() => { | ||||
|           context.closeModal(id); | ||||
|         }} | ||||
|       > | ||||
|         <Trans>Close modal</Trans> | ||||
|       </Button> | ||||
|     </Stack> | ||||
|   ); | ||||
| } | ||||
| @@ -10,6 +10,7 @@ import { ModalsProvider } from '@mantine/modals'; | ||||
| import { Notifications } from '@mantine/notifications'; | ||||
|  | ||||
| import { QrCodeModal } from '../components/modals/QrCodeModal'; | ||||
| import { ServerInfoModal } from '../components/modals/ServerInfoModal'; | ||||
| import { useLocalState } from '../states/LocalState'; | ||||
|  | ||||
| export function ThemeContext({ children }: { children: JSX.Element }) { | ||||
| @@ -60,7 +61,7 @@ export function ThemeContext({ children }: { children: JSX.Element }) { | ||||
|         <Notifications /> | ||||
|         <ModalsProvider | ||||
|           labels={{ confirm: t`Submit`, cancel: t`Cancel` }} | ||||
|           modals={{ qr: QrCodeModal }} | ||||
|           modals={{ qr: QrCodeModal, info: ServerInfoModal }} | ||||
|         > | ||||
|           {children} | ||||
|         </ModalsProvider> | ||||
|   | ||||
| @@ -8,7 +8,12 @@ export const emptyServerAPI = { | ||||
|   worker_running: null, | ||||
|   worker_pending_tasks: null, | ||||
|   plugins_enabled: null, | ||||
|   active_plugins: [] | ||||
|   active_plugins: [], | ||||
|   email_configured: null, | ||||
|   debug_mode: null, | ||||
|   docker_mode: null, | ||||
|   database: null, | ||||
|   system_health: null | ||||
| }; | ||||
|  | ||||
| export interface SiteMarkProps { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { openContextModal } from '@mantine/modals'; | ||||
|  | ||||
| import { DocumentationLinkItem } from '../components/items/DocumentationLinks'; | ||||
| import { IS_DEV_OR_DEMO } from '../main'; | ||||
| @@ -69,18 +70,26 @@ export const navDocLinks: DocumentationLinkItem[] = [ | ||||
|   } | ||||
| ]; | ||||
|  | ||||
| function serverInfo() { | ||||
|   return openContextModal({ | ||||
|     modal: 'info', | ||||
|     title: <Trans>System Information</Trans>, | ||||
|     size: 'xl', | ||||
|     innerProps: {} | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // TODO @matmair: Add the following pages and adjust the links | ||||
| export const aboutLinks: DocumentationLinkItem[] = [ | ||||
|   { | ||||
|     id: 'instance', | ||||
|     title: <Trans>Instance</Trans>, | ||||
|     title: <Trans>System Information</Trans>, | ||||
|     description: <Trans>About this Inventree instance</Trans>, | ||||
|     link: '/instance', | ||||
|     placeholder: true | ||||
|     action: serverInfo | ||||
|   }, | ||||
|   { | ||||
|     id: 'about', | ||||
|     title: <Trans>InvenTree</Trans>, | ||||
|     title: <Trans>About InvenTree</Trans>, | ||||
|     description: <Trans>About the InvenTree org</Trans>, | ||||
|     link: '/about', | ||||
|     placeholder: true | ||||
|   | ||||
| @@ -28,6 +28,11 @@ export interface ServerAPIProps { | ||||
|   worker_pending_tasks: null | number; | ||||
|   plugins_enabled: null | boolean; | ||||
|   active_plugins: PluginProps[]; | ||||
|   email_configured: null | boolean; | ||||
|   debug_mode: null | boolean; | ||||
|   docker_mode: null | boolean; | ||||
|   database: null | string; | ||||
|   system_health: null | boolean; | ||||
| } | ||||
|  | ||||
| // Type interface defining a single 'setting' object | ||||
|   | ||||
		Reference in New Issue
	
	Block a user