Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Admin view-logs page #17276

Merged
merged 43 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9315e3b
Remove unused props in StringSettingInput
tassoevan Apr 1, 2020
e9f7651
Softly deprecate Paragraph
tassoevan Apr 1, 2020
b1792bd
Remove props from createTemplateForComponent
tassoevan Apr 2, 2020
51080ff
Simplify createTemplateForComponent
tassoevan Apr 2, 2020
ee7aaa0
Add renderRouteComponent
tassoevan Apr 2, 2020
e1c79df
Fix concurrency issues with FlowRouter
tassoevan Apr 2, 2020
0e03026
Update Engagement Dashboard routes
tassoevan Apr 2, 2020
b0a7c8d
Update createTemplateForComponent
tassoevan Apr 3, 2020
f2d586e
Refactor ModalBlock
tassoevan Apr 4, 2020
60e41d0
Refactor MessageBlock
tassoevan Apr 4, 2020
22bb34e
Remove unused modules
tassoevan Apr 4, 2020
78038e2
Refactor RoomForeword
tassoevan Apr 4, 2020
50f7f15
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into feat…
tassoevan Apr 4, 2020
bbd0dad
Fix some container views
tassoevan Apr 4, 2020
6cce911
Merge branch 'develop' into feat/react-root
ggazzo Apr 6, 2020
d2cbd60
creating viewlogs component
dudizilla Apr 13, 2020
8fcc8c2
creating viewlogs component
dudizilla Apr 13, 2020
2aa265a
Merge branch 'develop' into view-logs
dudizilla Apr 13, 2020
8816458
resolving import errors
dudizilla Apr 13, 2020
396f91a
Merge branch 'view-logs' into view-logs
dudizilla Apr 13, 2020
52d34f6
resolving lint errors
dudizilla Apr 13, 2020
41f0195
moving view-logs to the correct folder
dudizilla Apr 13, 2020
960d7dd
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
dudizilla Apr 16, 2020
f4a93a1
Merge branch 'develop' into view-logs
ggazzo Apr 16, 2020
28656d9
help
ggazzo Apr 16, 2020
584d60e
Merge branch 'view-logs' of https://github.com/RocketChat/Rocket.Chat…
dudizilla Apr 17, 2020
7da2e2f
Update routes.js
ggazzo Apr 17, 2020
69ca252
Update ViewLogsRoute.js
ggazzo Apr 17, 2020
29683da
Update viewLogs.js
ggazzo Apr 17, 2020
68342c7
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
dudizilla Apr 20, 2020
556f995
Tasso changes
dudizilla Apr 20, 2020
9ca4379
Upadted meteor Streamer mock
dudizilla Apr 20, 2020
b1f3439
Rewriting JS view-logs functions
dudizilla Apr 20, 2020
6ea7f63
Adding decorators on ViewLogs.stories
dudizilla Apr 20, 2020
7dd9ff9
refatoring canAccess permission
dudizilla Apr 20, 2020
902da54
Resolving layout issues on Page component
dudizilla Apr 20, 2020
c690660
removing unused files
dudizilla Apr 21, 2020
e2293ca
refactoring index.js
dudizilla Apr 21, 2020
f20af41
Index.js renamed to ViewLogs.js
dudizilla Apr 21, 2020
7ee174f
moving viewlogs stories
dudizilla Apr 21, 2020
2a42362
removing unused variables
dudizilla Apr 21, 2020
949ae85
Merge branch 'view-logs' of https://github.com/RocketChat/Rocket.Chat…
dudizilla Apr 21, 2020
41c002c
Merge branch 'develop' into view-logs
tassoevan Apr 21, 2020
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
5 changes: 4 additions & 1 deletion .storybook/mocks/meteor.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ export const Meteor = {
_localStorage: window.localStorage,
absoluteUrl: () => {},
userId: () => {},
Streamer: () => {},
Streamer: () => ({
on: () => {},
removeListener: () => {},
}),
startup: () => {},
methods: () => {},
call: () => {},
Expand Down
34 changes: 0 additions & 34 deletions app/logger/client/ansispan.js

This file was deleted.

245 changes: 245 additions & 0 deletions app/logger/client/components/ViewLogs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { Meteor } from 'meteor/meteor';
import React, { useEffect, useRef, useState, useCallback } from 'react';
import { Box, Scrollable } from '@rocket.chat/fuselage';

import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext';
import { useEndpoint } from '../../../../client/contexts/ServerContext';
import { useTranslation } from '../../../../client/contexts/TranslationContext';
import { Page } from '../../../../client/components/basic/Page';

const foregroundColors = {
30: 'gray',
31: 'red',
32: 'lime',
33: 'yellow',
34: '#6B98FF',
35: '#FF00FF',
36: 'cyan',
37: 'white',
};

const ansispan = (str) => {
str = str
.replace(/\s/g, ' ')
.replace(/(\\n|\n)/g, '<br>')
.replace(/>/g, '&gt;')
.replace(/</g, '&lt;')
.replace(/(.\d{8}-\d\d:\d\d:\d\d\.\d\d\d\(?.{0,2}\)?)/, '<span>$1</span>')
.replace(/\033\[1m/g, '<strong>')
.replace(/\033\[22m/g, '</strong>')
.replace(/\033\[3m/g, '<em>')
.replace(/\033\[23m/g, '</em>')
.replace(/\033\[m/g, '</span>')
.replace(/\033\[0m/g, '</span>')
.replace(/\033\[39m/g, '</span>');
return Object.entries(foregroundColors).reduce((str, [ansiCode, color]) => {
const span = `<span style="color: ${ color }">`;
return (
str
.replace(new RegExp(`\\033\\[${ ansiCode }m`, 'g'), span)
.replace(new RegExp(`\\033\\[0;${ ansiCode }m`, 'g'), span)
);
}, str);
};

function ViewLogs() {
const [lines, setLines] = useState([]);
window.setLines = setLines;

const dispatchToastMessage = useToastMessageDispatch();

const getStdoutQueue = useEndpoint('GET', 'stdout.queue');

useEffect(() => {
const fetchLines = async () => {
try {
const { queue } = await getStdoutQueue();
setLines(queue);
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
};

fetchLines();
}, []);

useEffect(() => {
const stdoutStreamer = new Meteor.Streamer('stdout');

const handleStdout = (line) => {
setLines((lines) => [...lines, line]);
};

stdoutStreamer.on('stdout', handleStdout);

return () => {
stdoutStreamer.removeListener('stdout');
};
}, []);

const t = useTranslation();

const wrapperRef = useRef();
const atBottomRef = useRef();

const [newLogsVisible, setNewLogsVisible] = useState(false);

const isAtBottom = useCallback((scrollThreshold) => {
const wrapper = wrapperRef.current;

if (scrollThreshold == null) {
scrollThreshold = 0;
}
if (wrapper.scrollTop + scrollThreshold >= wrapper.scrollHeight - wrapper.clientHeight) {
setNewLogsVisible(false);
return true;
}
return false;
}, []);

const sendToBottom = useCallback(() => {
const wrapper = wrapperRef.current;

wrapper.scrollTop = wrapper.scrollHeight - wrapper.clientHeight;
setNewLogsVisible(false);
}, []);

const checkIfScrollIsAtBottom = useCallback(() => {
atBottomRef.current = isAtBottom(100);
}, [isAtBottom]);

const sendToBottomIfNecessary = useCallback(() => {
if (atBottomRef.current === true && isAtBottom() !== true) {
sendToBottom();
} else if (atBottomRef.current === false) {
setNewLogsVisible(true);
}
}, [isAtBottom, sendToBottom]);

useEffect(() => {
if (window.MutationObserver) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(() => {
sendToBottomIfNecessary();
});
});
observer.observe(wrapperRef.current, { childList: true });

return () => {
observer.disconnect();
};
}

const handleSubtreeModified = () => {
sendToBottomIfNecessary();
};
wrapperRef.current.addEventListener('DOMSubtreeModified', handleSubtreeModified);

return () => {
wrapperRef.current.removeEventListener('DOMSubtreeModified', handleSubtreeModified);
};
}, [sendToBottomIfNecessary]);

useEffect(() => {
const handleWindowResize = () => {
setTimeout(() => {
sendToBottomIfNecessary();
}, 100);
};

window.addEventListener('resize', handleWindowResize);

return () => {
window.removeEventListener('resize', handleWindowResize);
};
}, [sendToBottomIfNecessary]);

const handleWheel = useCallback(() => {
atBottomRef.current = false;
setTimeout(() => {
checkIfScrollIsAtBottom();
}, 100);
}, [checkIfScrollIsAtBottom]);

const handleTouchStart = () => {
atBottomRef.current = false;
};

const handleTouchEnd = useCallback(() => {
setTimeout(() => {
checkIfScrollIsAtBottom();
}, 100);
}, [checkIfScrollIsAtBottom]);

const handleScroll = useCallback(() => {
atBottomRef.current = false;
setTimeout(() => {
checkIfScrollIsAtBottom();
}, 100);
}, [checkIfScrollIsAtBottom]);

const handleClick = useCallback(() => {
atBottomRef.current = true;
sendToBottomIfNecessary();
}, [sendToBottomIfNecessary]);

return <Page>
<Page.Header title={t('View Logs')} />
<Page.Content>
<Box
width='full'
height='full'
style={{ overflow: 'hidden', position: 'relative' }}
display='flex'
// flexGrow={1}
>
<Scrollable vertical>
<Box
ref={wrapperRef}
margin='none'
paddingBlock='8px'
paddingInline='10px'
flexGrow={1}
textStyle='mono'
textColor='alternative'
style={{ backgroundColor: '#444444' }}
onWheel={handleWheel}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onScroll={handleScroll}
>
{lines.sort((a, b) => a.ts - b.ts).map(({ string }, i) =>
<Box
key={i}
style={{ wordBreak: 'break-all' }}
dangerouslySetInnerHTML={{ __html: ansispan(string) }}
/>)}
</Box>
</Scrollable>
<Box
onClick={handleClick}
style={{ position: 'absolute',
bottom: '8px',
left: '50%',
width: '130px',
height: '30px',
margin: 'o -65px',
cursor: 'pointer',
transition: 'transform 0.3s ease-out',
transform: newLogsVisible ? 'translateY(0)' : 'translateY(150%)',
textAlign: 'center',
borderRadius: '20px',
fontSize: '0.8em',
lineHeight: '30px',
color: 'var(--color-blue)',
backgroundColor: '#FFF' }}
>
<i className='icon-down-big' />
<span>{t('New_logs')}</span>
</Box>
</Box>
</Page.Content>
</Page>;
}

export default ViewLogs;
16 changes: 16 additions & 0 deletions app/logger/client/components/ViewLogs.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

import ViewLogs from './ViewLogs';

export default {
title: 'admin/pages/ViewLogs',
component: ViewLogs,
decorators: [
(storyFn) => <div className='rc-old' style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
{storyFn()}
</div>,
],
};

export const _default = () =>
<ViewLogs />;
15 changes: 15 additions & 0 deletions app/logger/client/components/ViewLogsRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import { NotAuthorizedPage } from '../../../ui-admin/client/components/NotAuthorizedPage';
import { usePermission } from '../../../../client/contexts/AuthorizationContext';
import ViewLogs from './ViewLogs';

export default function ViewLogsRoute() {
const canViewLogs = usePermission('view-logs');

if (!canViewLogs) {
return <NotAuthorizedPage />;
}

return <ViewLogs />;
}
16 changes: 15 additions & 1 deletion app/logger/client/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
import './logger';
import './viewLogs';

import { hasAllPermission } from '../../authorization';
import { registerAdminRoute, registerAdminSidebarItem } from '../../ui-admin/client';

registerAdminSidebarItem({
href: 'admin-view-logs',
i18nLabel: 'View_Logs',
icon: 'post',
permissionGranted: () => hasAllPermission('view-logs'),
});

registerAdminRoute('/view-logs', {
name: 'admin-view-logs',
lazyRouteComponent: () => import('./components/ViewLogsRoute'),
});
33 changes: 0 additions & 33 deletions app/logger/client/viewLogs.js

This file was deleted.

Loading