mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
Merge pull request 'fix/admin-panel' (#65) from fix/admin-panel into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/server/pulls/65
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 144 KiB |
@ -1,18 +1,17 @@
|
|||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useDispatch} from 'react-redux';
|
||||||
import {useLocation, useNavigate} from 'react-router-dom';
|
import {useLocation, useNavigate} from 'react-router-dom';
|
||||||
import {selectIsAuthenticated} from '../../store/slices/userSlice';
|
|
||||||
import {clearConfig} from '../../store/slices/configSlice';
|
import {clearConfig} from '../../store/slices/configSlice';
|
||||||
import {logout} from '../../api';
|
import {logout} from '../../api';
|
||||||
import MenuItem from './MenuItem/MenuItem';
|
import MenuItem from './MenuItem/MenuItem';
|
||||||
import AppMenuLogo from '../../assets/AppMenuLogo.svg';
|
import AppMenuLogo from '../../assets/AppMenuLogo.svg';
|
||||||
import {menuItems} from '../../config/menuItems';
|
import {menuItems} from '../../config/menuItems';
|
||||||
import styles from './Menu.module.scss';
|
import styles from './Menu.module.scss';
|
||||||
|
import FileIcon from '../../assets/File.svg';
|
||||||
|
|
||||||
function Menu() {
|
function Menu() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
@ -49,17 +48,16 @@ function Menu() {
|
|||||||
|
|
||||||
<div className={styles['menu__menuItems']}>
|
<div className={styles['menu__menuItems']}>
|
||||||
{menuItems.map(item => (
|
{menuItems.map(item => (
|
||||||
<MenuItem key={item.key} label={item.label} isActive={isActiveItem(item.path)} onClick={() => handleMenuItemClick(item)} />
|
<MenuItem
|
||||||
|
key={item.key}
|
||||||
|
label={item.label}
|
||||||
|
isActive={isActiveItem(item.path)}
|
||||||
|
onClick={() => handleMenuItemClick(item)}
|
||||||
|
icon={FileIcon}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
<MenuItem label='Logout' isActive={false} onClick={handleLogout} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isAuthenticated && (
|
|
||||||
<div className={styles['menu__logoutContainer']}>
|
|
||||||
<button onClick={handleLogout} className={styles['menu__logoutButton']}>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -60,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__logoutContainer {
|
&__logoutContainer {
|
||||||
margin-top: auto;
|
margin-left: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__logoutButton {
|
&__logoutButton {
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import FileIcon from '../../../assets/File.svg';
|
|
||||||
import styles from './MenuItem.module.scss';
|
import styles from './MenuItem.module.scss';
|
||||||
|
|
||||||
function MenuItem({label, isActive, onClick}) {
|
function MenuItem({label, isActive, onClick, icon}) {
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.menuItem} ${isActive ? styles['menuItem--active'] : ''}`} onClick={onClick}>
|
<div className={`${styles.menuItem} ${isActive ? styles['menuItem--active'] : ''}`} onClick={onClick}>
|
||||||
<img src={FileIcon} alt='' className={styles['menuItem__icon']} />
|
{icon ? <img src={icon} alt='' className={styles['menuItem__icon']} /> : <div className={styles['menuItem__icon']} />}
|
||||||
<span className={styles['menuItem__label']}>{label}</span>
|
<span className={styles['menuItem__label']}>{label}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&--disabled {
|
&--disabled {
|
||||||
background: #ff865c;
|
background: #ffd4c5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -137,34 +137,34 @@ function Expiration() {
|
|||||||
<div className={styles.tabPanel}>
|
<div className={styles.tabPanel}>
|
||||||
<div className={styles.formRow}>
|
<div className={styles.formRow}>
|
||||||
<Input
|
<Input
|
||||||
label='Files Cron Expression'
|
label='Cache Cleanup Cron Expression'
|
||||||
value={localSettings.filesCron}
|
value={localSettings.filesCron}
|
||||||
onChange={value => handleFieldChange('filesCron', value)}
|
onChange={value => handleFieldChange('filesCron', value)}
|
||||||
placeholder='0 0 */2 * * *'
|
placeholder='0 0 */2 * * *'
|
||||||
description='Cron expression for file cleanup schedule (6 fields: second minute hour day month day_of_week)'
|
description='Cron expression for cleaning up expired cached files and temporary data (6 fields: second minute hour day month day_of_week)'
|
||||||
error={getFieldError(CONFIG_PATHS.filesCron)}
|
error={getFieldError(CONFIG_PATHS.filesCron)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.formRow}>
|
<div className={styles.formRow}>
|
||||||
<Input
|
<Input
|
||||||
label='Documents Cron Expression'
|
label='Auto-Save & Presence Cleanup Cron Expression'
|
||||||
value={localSettings.documentsCron}
|
value={localSettings.documentsCron}
|
||||||
onChange={value => handleFieldChange('documentsCron', value)}
|
onChange={value => handleFieldChange('documentsCron', value)}
|
||||||
placeholder='0 0 */2 * * *'
|
placeholder='0 0 */2 * * *'
|
||||||
description='Cron expression for document cleanup schedule (6 fields: second minute hour day month day_of_week)'
|
description='Cron expression for auto-saving documents with pending changes and cleaning up expired user presence data (6 fields: second minute hour day month day_of_week)'
|
||||||
error={getFieldError(CONFIG_PATHS.documentsCron)}
|
error={getFieldError(CONFIG_PATHS.documentsCron)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.formRow}>
|
<div className={styles.formRow}>
|
||||||
<Input
|
<Input
|
||||||
label='Files Expiration Time (seconds)'
|
label='Cache File Retention Time (seconds)'
|
||||||
type='number'
|
type='number'
|
||||||
value={localSettings.files}
|
value={localSettings.files}
|
||||||
onChange={value => handleFieldChange('files', value)}
|
onChange={value => handleFieldChange('files', value)}
|
||||||
placeholder='3600'
|
placeholder='3600'
|
||||||
description='Time in seconds after which files expire and can be cleaned up'
|
description='How long to keep cached files before marking them as expired and eligible for cleanup (default: 86400 = 24 hours)'
|
||||||
min='0'
|
min='0'
|
||||||
error={getFieldError(CONFIG_PATHS.files)}
|
error={getFieldError(CONFIG_PATHS.files)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export default function Login() {
|
|||||||
onChange={setTenantName}
|
onChange={setTenantName}
|
||||||
placeholder='Enter your tenant name'
|
placeholder='Enter your tenant name'
|
||||||
description='The name of your tenant organization'
|
description='The name of your tenant organization'
|
||||||
|
error={error}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -107,3 +107,6 @@ app.use((err, req, res, _next) => {
|
|||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
operationContext.global.logger.warn('AdminPanel server listening on port %d', port);
|
operationContext.global.logger.warn('AdminPanel server listening on port %d', port);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//after all required modules in all files
|
||||||
|
moduleReloader.finalizeConfigWithRuntime();
|
||||||
|
|||||||
@ -50,6 +50,10 @@ function reloadNpmModule(moduleName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backup original NODE_CONFIG to avoid growing environment
|
||||||
|
const prevNodeConfig = process.env.NODE_CONFIG;
|
||||||
|
let nodeConfigOverridden = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires config module with runtime configuration support.
|
* Requires config module with runtime configuration support.
|
||||||
* Temporarily sets NODE_CONFIG for reload, then restores environment to prevent E2BIG.
|
* Temporarily sets NODE_CONFIG for reload, then restores environment to prevent E2BIG.
|
||||||
@ -59,10 +63,6 @@ function reloadNpmModule(moduleName) {
|
|||||||
function requireConfigWithRuntime(opt_additionalConfig) {
|
function requireConfigWithRuntime(opt_additionalConfig) {
|
||||||
let config = require('config');
|
let config = require('config');
|
||||||
|
|
||||||
// Backup original NODE_CONFIG to avoid growing environment
|
|
||||||
const prevNodeConfig = process.env.NODE_CONFIG;
|
|
||||||
let nodeConfigOverridden = false;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const configFilePath = config.get('runtimeConfig.filePath');
|
const configFilePath = config.get('runtimeConfig.filePath');
|
||||||
if (configFilePath) {
|
if (configFilePath) {
|
||||||
@ -88,20 +88,23 @@ function requireConfigWithRuntime(opt_additionalConfig) {
|
|||||||
if (err.code !== 'ENOENT') {
|
if (err.code !== 'ENOENT') {
|
||||||
console.error('Failed to load runtime config: %s', err.stack);
|
console.error('Failed to load runtime config: %s', err.stack);
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
// Restore original NODE_CONFIG to keep env small and avoid E2BIG on Windows/pkg
|
|
||||||
if (nodeConfigOverridden) {
|
|
||||||
if (typeof prevNodeConfig === 'undefined') {
|
|
||||||
delete process.env.NODE_CONFIG;
|
|
||||||
} else {
|
|
||||||
process.env.NODE_CONFIG = prevNodeConfig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function finalizeConfigWithRuntime() {
|
||||||
|
// Restore original NODE_CONFIG to keep env small and avoid E2BIG on Windows/pkg
|
||||||
|
if (nodeConfigOverridden) {
|
||||||
|
if (typeof prevNodeConfig === 'undefined') {
|
||||||
|
delete process.env.NODE_CONFIG;
|
||||||
|
} else {
|
||||||
|
process.env.NODE_CONFIG = prevNodeConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
reloadNpmModule,
|
reloadNpmModule,
|
||||||
requireConfigWithRuntime
|
requireConfigWithRuntime,
|
||||||
|
finalizeConfigWithRuntime
|
||||||
};
|
};
|
||||||
|
|||||||
@ -509,3 +509,6 @@ process.on('uncaughtException', err => {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//after all required modules in all files
|
||||||
|
moduleReloader.finalizeConfigWithRuntime();
|
||||||
|
|||||||
@ -66,7 +66,7 @@ function initializeSharp() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharp) {
|
if (sharp) {
|
||||||
// todo test.
|
// todo test.
|
||||||
// Set concurrency to 2 for better performance
|
// Set concurrency to 2 for better performance
|
||||||
|
|||||||
@ -33,10 +33,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const cluster = require('cluster');
|
const cluster = require('cluster');
|
||||||
const logger = require('./../../Common/sources/logger');
|
|
||||||
const operationContext = require('./../../Common/sources/operationContext');
|
|
||||||
const moduleReloader = require('./../../Common/sources/moduleReloader');
|
const moduleReloader = require('./../../Common/sources/moduleReloader');
|
||||||
const config = moduleReloader.requireConfigWithRuntime();
|
const config = moduleReloader.requireConfigWithRuntime();
|
||||||
|
const logger = require('./../../Common/sources/logger');
|
||||||
|
const operationContext = require('./../../Common/sources/operationContext');
|
||||||
|
|
||||||
if (cluster.isMaster) {
|
if (cluster.isMaster) {
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@ -104,3 +104,6 @@ process.on('uncaughtException', err => {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//after all required modules in all files
|
||||||
|
moduleReloader.finalizeConfigWithRuntime();
|
||||||
|
|||||||
Reference in New Issue
Block a user