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 {selectIsAuthenticated} from '../../store/slices/userSlice';
|
||||
import {clearConfig} from '../../store/slices/configSlice';
|
||||
import {logout} from '../../api';
|
||||
import MenuItem from './MenuItem/MenuItem';
|
||||
import AppMenuLogo from '../../assets/AppMenuLogo.svg';
|
||||
import {menuItems} from '../../config/menuItems';
|
||||
import styles from './Menu.module.scss';
|
||||
import FileIcon from '../../assets/File.svg';
|
||||
|
||||
function Menu() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
@ -49,17 +48,16 @@ function Menu() {
|
||||
|
||||
<div className={styles['menu__menuItems']}>
|
||||
{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>
|
||||
|
||||
{isAuthenticated && (
|
||||
<div className={styles['menu__logoutContainer']}>
|
||||
<button onClick={handleLogout} className={styles['menu__logoutButton']}>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
}
|
||||
|
||||
&__logoutContainer {
|
||||
margin-top: auto;
|
||||
margin-left: 48px;
|
||||
}
|
||||
|
||||
&__logoutButton {
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import FileIcon from '../../../assets/File.svg';
|
||||
import styles from './MenuItem.module.scss';
|
||||
|
||||
function MenuItem({label, isActive, onClick}) {
|
||||
function MenuItem({label, isActive, onClick, icon}) {
|
||||
return (
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background: #ff865c;
|
||||
background: #ffd4c5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
@ -137,34 +137,34 @@ function Expiration() {
|
||||
<div className={styles.tabPanel}>
|
||||
<div className={styles.formRow}>
|
||||
<Input
|
||||
label='Files Cron Expression'
|
||||
label='Cache Cleanup Cron Expression'
|
||||
value={localSettings.filesCron}
|
||||
onChange={value => handleFieldChange('filesCron', value)}
|
||||
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)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.formRow}>
|
||||
<Input
|
||||
label='Documents Cron Expression'
|
||||
label='Auto-Save & Presence Cleanup Cron Expression'
|
||||
value={localSettings.documentsCron}
|
||||
onChange={value => handleFieldChange('documentsCron', value)}
|
||||
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)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.formRow}>
|
||||
<Input
|
||||
label='Files Expiration Time (seconds)'
|
||||
label='Cache File Retention Time (seconds)'
|
||||
type='number'
|
||||
value={localSettings.files}
|
||||
onChange={value => handleFieldChange('files', value)}
|
||||
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'
|
||||
error={getFieldError(CONFIG_PATHS.files)}
|
||||
/>
|
||||
|
||||
@ -46,6 +46,7 @@ export default function Login() {
|
||||
onChange={setTenantName}
|
||||
placeholder='Enter your tenant name'
|
||||
description='The name of your tenant organization'
|
||||
error={error}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -107,3 +107,6 @@ app.use((err, req, res, _next) => {
|
||||
server.listen(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.
|
||||
* Temporarily sets NODE_CONFIG for reload, then restores environment to prevent E2BIG.
|
||||
@ -59,10 +63,6 @@ function reloadNpmModule(moduleName) {
|
||||
function requireConfigWithRuntime(opt_additionalConfig) {
|
||||
let config = require('config');
|
||||
|
||||
// Backup original NODE_CONFIG to avoid growing environment
|
||||
const prevNodeConfig = process.env.NODE_CONFIG;
|
||||
let nodeConfigOverridden = false;
|
||||
|
||||
try {
|
||||
const configFilePath = config.get('runtimeConfig.filePath');
|
||||
if (configFilePath) {
|
||||
@ -88,20 +88,23 @@ function requireConfigWithRuntime(opt_additionalConfig) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
reloadNpmModule,
|
||||
requireConfigWithRuntime
|
||||
requireConfigWithRuntime,
|
||||
finalizeConfigWithRuntime
|
||||
};
|
||||
|
||||
@ -509,3 +509,6 @@ process.on('uncaughtException', err => {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
//after all required modules in all files
|
||||
moduleReloader.finalizeConfigWithRuntime();
|
||||
|
||||
@ -66,7 +66,7 @@ function initializeSharp() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (sharp) {
|
||||
// todo test.
|
||||
// Set concurrency to 2 for better performance
|
||||
|
||||
@ -33,10 +33,10 @@
|
||||
'use strict';
|
||||
|
||||
const cluster = require('cluster');
|
||||
const logger = require('./../../Common/sources/logger');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
const moduleReloader = require('./../../Common/sources/moduleReloader');
|
||||
const config = moduleReloader.requireConfigWithRuntime();
|
||||
const logger = require('./../../Common/sources/logger');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
|
||||
if (cluster.isMaster) {
|
||||
const fs = require('fs');
|
||||
@ -104,3 +104,6 @@ process.on('uncaughtException', err => {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
//after all required modules in all files
|
||||
moduleReloader.finalizeConfigWithRuntime();
|
||||
|
||||
Reference in New Issue
Block a user