mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-04-07 14:04:35 +08:00
Merge pull request 'fix/admin-panel-bugs-4' (#75) from fix/admin-panel-bugs-4 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/server/pulls/75
This commit is contained in:
@ -1,6 +1,5 @@
|
|||||||
import {useEffect, useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
import {useLocation, useNavigate} from 'react-router-dom';
|
|
||||||
import {fetchUser, selectUser, selectUserLoading, selectIsAuthenticated} from '../../store/slices/userSlice';
|
import {fetchUser, selectUser, selectUserLoading, selectIsAuthenticated} from '../../store/slices/userSlice';
|
||||||
import {checkSetupRequired} from '../../api';
|
import {checkSetupRequired} from '../../api';
|
||||||
import Spinner from '../../assets/Spinner.svg';
|
import Spinner from '../../assets/Spinner.svg';
|
||||||
@ -10,8 +9,6 @@ import ServerUnavailable from '../ServerUnavailable/ServerUnavailable';
|
|||||||
|
|
||||||
export default function AuthWrapper({children}) {
|
export default function AuthWrapper({children}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const user = useSelector(selectUser);
|
const user = useSelector(selectUser);
|
||||||
const loading = useSelector(selectUserLoading);
|
const loading = useSelector(selectUserLoading);
|
||||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
const isAuthenticated = useSelector(selectIsAuthenticated);
|
||||||
@ -20,24 +17,6 @@ export default function AuthWrapper({children}) {
|
|||||||
const [checkingSetup, setCheckingSetup] = useState(true);
|
const [checkingSetup, setCheckingSetup] = useState(true);
|
||||||
const [serverUnavailable, setServerUnavailable] = useState(false);
|
const [serverUnavailable, setServerUnavailable] = useState(false);
|
||||||
|
|
||||||
// Save intended URL for redirect after setup/login
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isAuthenticated && location.pathname !== '/admin/setup' && location.pathname !== '/admin/login') {
|
|
||||||
sessionStorage.setItem('redirectAfterAuth', location.pathname + location.search);
|
|
||||||
}
|
|
||||||
}, [location, isAuthenticated]);
|
|
||||||
|
|
||||||
// Redirect after successful authentication
|
|
||||||
useEffect(() => {
|
|
||||||
if (isAuthenticated && user) {
|
|
||||||
const redirectUrl = sessionStorage.getItem('redirectAfterAuth');
|
|
||||||
if (redirectUrl) {
|
|
||||||
sessionStorage.removeItem('redirectAfterAuth');
|
|
||||||
navigate(redirectUrl, {replace: true});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [isAuthenticated, user, navigate]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkSetup = async () => {
|
const checkSetup = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -194,6 +194,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
AI.onLoadInternalProviders();
|
AI.onLoadInternalProviders();
|
||||||
|
if (Asc.plugin.sendEvent)
|
||||||
|
Asc.plugin.sendEvent("ai_onLoadInternalProviders");
|
||||||
};
|
};
|
||||||
|
|
||||||
AI.InternalCustomProvidersSources = {};
|
AI.InternalCustomProvidersSources = {};
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
import {useState, useEffect, useCallback} from 'react';
|
import {useState, useEffect, useCallback} from 'react';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
import {selectConfig, saveConfig} from '../../../store/slices/configSlice';
|
import {selectConfig, saveConfig} from '../../../store/slices/configSlice';
|
||||||
import {registerShowWindowCallback, registerCloseWindowCallback, registerSaveCallback, initAISettings} from '../js/plugins-sdk';
|
import {
|
||||||
|
registerShowWindowCallback,
|
||||||
|
registerCloseWindowCallback,
|
||||||
|
registerSaveCallback,
|
||||||
|
registerLoadInternalProvidersCallback,
|
||||||
|
initAISettings
|
||||||
|
} from '../js/plugins-sdk';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hook for managing complete AI plugin functionality
|
* Custom hook for managing complete AI plugin functionality
|
||||||
@ -11,6 +17,7 @@ import {registerShowWindowCallback, registerCloseWindowCallback, registerSaveCal
|
|||||||
*/
|
*/
|
||||||
const useAiPlugin = statisticsData => {
|
const useAiPlugin = statisticsData => {
|
||||||
const [pluginWindows, setPluginWindows] = useState([]);
|
const [pluginWindows, setPluginWindows] = useState([]);
|
||||||
|
const [internalProvidersLoaded, setInternalProvidersLoaded] = useState(false);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const config = useSelector(selectConfig);
|
const config = useSelector(selectConfig);
|
||||||
|
|
||||||
@ -121,16 +128,22 @@ const useAiPlugin = statisticsData => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLoadInternalProviders = () => {
|
||||||
|
setInternalProvidersLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
// Register all callbacks with SDK
|
// Register all callbacks with SDK
|
||||||
registerShowWindowCallback(handleShowWindow);
|
registerShowWindowCallback(handleShowWindow);
|
||||||
registerCloseWindowCallback(handleCloseWindow);
|
registerCloseWindowCallback(handleCloseWindow);
|
||||||
registerSaveCallback(handleSave);
|
registerSaveCallback(handleSave);
|
||||||
|
registerLoadInternalProvidersCallback(handleLoadInternalProviders);
|
||||||
|
|
||||||
// Cleanup: unregister all callbacks
|
// Cleanup: unregister all callbacks
|
||||||
return () => {
|
return () => {
|
||||||
registerShowWindowCallback(null);
|
registerShowWindowCallback(null);
|
||||||
registerCloseWindowCallback(null);
|
registerCloseWindowCallback(null);
|
||||||
registerSaveCallback(null);
|
registerSaveCallback(null);
|
||||||
|
registerLoadInternalProvidersCallback(null);
|
||||||
};
|
};
|
||||||
}, [config, dispatch]);
|
}, [config, dispatch]);
|
||||||
|
|
||||||
@ -139,7 +152,8 @@ const useAiPlugin = statisticsData => {
|
|||||||
return {
|
return {
|
||||||
pluginWindows,
|
pluginWindows,
|
||||||
currentWindow,
|
currentWindow,
|
||||||
handleIframeLoad
|
handleIframeLoad,
|
||||||
|
internalProvidersLoaded
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function AiIntegration() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Use custom hook for complete AI plugin functionality
|
// Use custom hook for complete AI plugin functionality
|
||||||
const {currentWindow, handleIframeLoad} = useAiPlugin(data);
|
const {currentWindow, handleIframeLoad, internalProvidersLoaded} = useAiPlugin(data);
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const AI_IFRAME_SRC = `ai/index.html`;
|
const AI_IFRAME_SRC = `ai/index.html`;
|
||||||
@ -58,7 +58,8 @@ export default function AiIntegration() {
|
|||||||
return (
|
return (
|
||||||
<div style={{display: 'flex', flexDirection: 'column', height: '100%', position: 'relative'}}>
|
<div style={{display: 'flex', flexDirection: 'column', height: '100%', position: 'relative'}}>
|
||||||
<iframe id={AI_IFRAME_ID} title='AI Settings' src={AI_IFRAME_SRC} style={iframeStyle} onLoad={() => handleIframeLoad(AI_IFRAME_ID)} />
|
<iframe id={AI_IFRAME_ID} title='AI Settings' src={AI_IFRAME_SRC} style={iframeStyle} onLoad={() => handleIframeLoad(AI_IFRAME_ID)} />
|
||||||
{currentWindow && (
|
{!internalProvidersLoaded && <div>Please, wait...</div>}
|
||||||
|
{internalProvidersLoaded && currentWindow && (
|
||||||
<div key={currentWindow.iframeId} style={{width: '100%', height: '100%'}}>
|
<div key={currentWindow.iframeId} style={{width: '100%', height: '100%'}}>
|
||||||
<PageHeader>{currentWindow.description || ''} </PageHeader>
|
<PageHeader>{currentWindow.description || ''} </PageHeader>
|
||||||
<iframe id={currentWindow.iframeId} title={currentWindow.description || ''} src={currentWindow.url} style={pluginWindowStyle} />
|
<iframe id={currentWindow.iframeId} title={currentWindow.description || ''} src={currentWindow.url} style={pluginWindowStyle} />
|
||||||
|
|||||||
@ -9,6 +9,7 @@ const mainButtonId = 'settings.html';
|
|||||||
let showPluginWindowCallback = null;
|
let showPluginWindowCallback = null;
|
||||||
let closePluginWindowCallback = null;
|
let closePluginWindowCallback = null;
|
||||||
let saveCallback = null;
|
let saveCallback = null;
|
||||||
|
let loadInternalProvidersCallback = null;
|
||||||
|
|
||||||
let settingsButton = null;
|
let settingsButton = null;
|
||||||
|
|
||||||
@ -390,6 +391,11 @@ function handleMethod(data) {
|
|||||||
} else if (data.methodName === 'CloseWindow') {
|
} else if (data.methodName === 'CloseWindow') {
|
||||||
CloseWindow(data.data);
|
CloseWindow(data.data);
|
||||||
handleMethodReturn(undefined);
|
handleMethodReturn(undefined);
|
||||||
|
} else if (data.methodName === 'SendEvent') {
|
||||||
|
if (data.data && data.data[0] === 'ai_onLoadInternalProviders' && loadInternalProvidersCallback) {
|
||||||
|
loadInternalProvidersCallback();
|
||||||
|
}
|
||||||
|
handleMethodReturn(undefined);
|
||||||
} else {
|
} else {
|
||||||
handleMethodReturn(undefined);
|
handleMethodReturn(undefined);
|
||||||
}
|
}
|
||||||
@ -543,4 +549,12 @@ function registerSaveCallback(callback) {
|
|||||||
saveCallback = callback;
|
saveCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {initAISettings, registerShowWindowCallback, registerCloseWindowCallback, registerSaveCallback};
|
/**
|
||||||
|
* Registers callback for loading internal providers
|
||||||
|
* @param {function} callback - Function to call when internal providers should be loaded () => void
|
||||||
|
*/
|
||||||
|
function registerLoadInternalProvidersCallback(callback) {
|
||||||
|
loadInternalProvidersCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {initAISettings, registerShowWindowCallback, registerCloseWindowCallback, registerSaveCallback, registerLoadInternalProvidersCallback};
|
||||||
|
|||||||
@ -67,9 +67,9 @@ router.patch('/', validateJWT, rawFileParser, async (req, res) => {
|
|||||||
await runtimeConfigManager.saveConfig(ctx, validationResult.value);
|
await runtimeConfigManager.saveConfig(ctx, validationResult.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.initTenantCache();
|
const newConfig = await runtimeConfigManager.getConfig(ctx);
|
||||||
const filteredConfig = getScopedConfig(ctx);
|
|
||||||
res.status(200).json(filteredConfig);
|
res.status(200).json(newConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ctx.logger.error('Configuration save error: %s', error.stack);
|
ctx.logger.error('Configuration save error: %s', error.stack);
|
||||||
res.status(500).json({error: 'Internal server error', details: error.message});
|
res.status(500).json({error: 'Internal server error', details: error.message});
|
||||||
|
|||||||
@ -38,7 +38,7 @@ const utils = require('../../../../../Common/sources/utils');
|
|||||||
const runtimeConfigManager = require('../../../../../Common/sources/runtimeConfigManager');
|
const runtimeConfigManager = require('../../../../../Common/sources/runtimeConfigManager');
|
||||||
const tenantManager = require('../../../../../Common/sources/tenantManager');
|
const tenantManager = require('../../../../../Common/sources/tenantManager');
|
||||||
const {validateJWT} = require('../../middleware/auth');
|
const {validateJWT} = require('../../middleware/auth');
|
||||||
const {getScopedConfig} = require('../config/config.service');
|
const {getConfig} = require('../../../../../Common/sources/runtimeConfigManager');
|
||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require('cookie-parser');
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@ -156,8 +156,8 @@ router.post('/rotate-keys', validateJWT, express.json(), async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
ctx.logger.info('WOPI key rotation start');
|
ctx.logger.info('WOPI key rotation start');
|
||||||
|
|
||||||
const currentConfig = ctx.getFullCfg();
|
const currentConfig = await getConfig(ctx);
|
||||||
const wopiConfig = utils.getImpl(currentConfig, 'wopi') || {};
|
const wopiConfig = currentConfig.wopi || {};
|
||||||
|
|
||||||
const newWopiConfig = generateWopiKeys();
|
const newWopiConfig = generateWopiKeys();
|
||||||
|
|
||||||
@ -184,9 +184,7 @@ router.post('/rotate-keys', validateJWT, express.json(), async (req, res) => {
|
|||||||
await runtimeConfigManager.saveConfig(ctx, newConfig);
|
await runtimeConfigManager.saveConfig(ctx, newConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.initTenantCache();
|
res.status(200).json(newConfig);
|
||||||
const filteredConfig = getScopedConfig(ctx);
|
|
||||||
res.status(200).json(filteredConfig);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ctx.logger.error('WOPI key rotation error: %s', error.stack);
|
ctx.logger.error('WOPI key rotation error: %s', error.stack);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
|
|||||||
@ -201,7 +201,7 @@ function concatParams(...parameters) {
|
|||||||
function getTableColumns(ctx, tableName) {
|
function getTableColumns(ctx, tableName) {
|
||||||
const values = [];
|
const values = [];
|
||||||
const sqlParam = addSqlParameter(tableName, values);
|
const sqlParam = addSqlParameter(tableName, values);
|
||||||
const sqlCommand = `SELECT column_name FROM information_schema.COLUMNS WHERE TABLE_NAME = ${sqlParam} AND TABLE_SCHEMA = 'dbo';`;
|
const sqlCommand = `SELECT column_name FROM information_schema.COLUMNS WHERE TABLE_NAME = ${sqlParam} AND TABLE_SCHEMA = SCHEMA_NAME();`;
|
||||||
return executeQuery(ctx, sqlCommand, values);
|
return executeQuery(ctx, sqlCommand, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user