mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-04-07 14:04:35 +08:00
[fix] Add shutdown requests proxing; Refactor settings
This commit is contained in:
@ -194,7 +194,7 @@ export const checkHealth = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getMaintenanceStatus = async () => {
|
export const getMaintenanceStatus = async () => {
|
||||||
const response = await safeFetch(`${DOCSERVICE_URL}/internal/cluster/inactive`, {
|
const response = await safeFetch(`${API_BASE_PATH}/docservice/shutdown`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
@ -205,7 +205,7 @@ export const getMaintenanceStatus = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const enterMaintenanceMode = async () => {
|
export const enterMaintenanceMode = async () => {
|
||||||
const response = await safeFetch(`${DOCSERVICE_URL}/internal/cluster/inactive`, {
|
const response = await safeFetch(`${API_BASE_PATH}/docservice/shutdown`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
@ -216,7 +216,7 @@ export const enterMaintenanceMode = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const exitMaintenanceMode = async () => {
|
export const exitMaintenanceMode = async () => {
|
||||||
const response = await safeFetch(`${DOCSERVICE_URL}/internal/cluster/inactive`, {
|
const response = await safeFetch(`${API_BASE_PATH}/docservice/shutdown`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
|
|||||||
@ -39,10 +39,12 @@ const Button = forwardRef(({onClick, children = 'Save Changes', disabled = false
|
|||||||
|
|
||||||
const getButtonClass = () => {
|
const getButtonClass = () => {
|
||||||
let buttonClass = styles.button;
|
let buttonClass = styles.button;
|
||||||
|
|
||||||
if (disabled && state === 'idle') buttonClass += ` ${styles['button--disabled']}`;
|
if (disabled && state === 'idle') buttonClass += ` ${styles['button--disabled']}`;
|
||||||
if (state === 'loading') buttonClass += ` ${styles['button--loading']}`;
|
if (state === 'loading') buttonClass += ` ${styles['button--loading']}`;
|
||||||
if (state === 'success') buttonClass += ` ${styles['button--success']}`;
|
if (state === 'success') buttonClass += ` ${styles['button--success']}`;
|
||||||
if (state === 'error') buttonClass += ` ${styles['button--error']}`;
|
if (state === 'error') buttonClass += ` ${styles['button--error']}`;
|
||||||
|
|
||||||
if (className) buttonClass += ` ${className}`;
|
if (className) buttonClass += ` ${className}`;
|
||||||
return buttonClass;
|
return buttonClass;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import {useMemo, useState} from 'react';
|
import {useMemo} from 'react';
|
||||||
import {useQuery} from '@tanstack/react-query';
|
import {useQuery} from '@tanstack/react-query';
|
||||||
import {fetchConfiguration} from '../../api';
|
import {fetchConfiguration} from '../../api';
|
||||||
|
import Button from '../Button/Button';
|
||||||
import styles from './ConfigViewer.module.scss';
|
import styles from './ConfigViewer.module.scss';
|
||||||
|
|
||||||
const ConfigViewer = () => {
|
const ConfigViewer = () => {
|
||||||
const [copySuccess, setCopySuccess] = useState(false);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: config,
|
data: config,
|
||||||
isLoading,
|
isLoading,
|
||||||
@ -23,13 +22,7 @@ const ConfigViewer = () => {
|
|||||||
|
|
||||||
const copyToClipboard = async () => {
|
const copyToClipboard = async () => {
|
||||||
if (!jsonString) return;
|
if (!jsonString) return;
|
||||||
try {
|
await navigator.clipboard.writeText(jsonString);
|
||||||
await navigator.clipboard.writeText(jsonString);
|
|
||||||
setCopySuccess(true);
|
|
||||||
setTimeout(() => setCopySuccess(false), 2000);
|
|
||||||
} catch {
|
|
||||||
// Clipboard API may fail on HTTP or restricted contexts
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@ -50,9 +43,7 @@ const ConfigViewer = () => {
|
|||||||
<p className={styles.description}>
|
<p className={styles.description}>
|
||||||
Sensitive parameters (passwords, keys, secrets) are shown as <span className={styles.redactedBadge}>REDACTED</span>.
|
Sensitive parameters (passwords, keys, secrets) are shown as <span className={styles.redactedBadge}>REDACTED</span>.
|
||||||
</p>
|
</p>
|
||||||
<button className={styles.copyButton} onClick={copyToClipboard}>
|
<Button onClick={copyToClipboard}>Copy JSON</Button>
|
||||||
{copySuccess ? '✓ Copied!' : 'Copy JSON'}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.configContent}>
|
<div className={styles.configContent}>
|
||||||
<pre className={styles.jsonPre}>{jsonString}</pre>
|
<pre className={styles.jsonPre}>{jsonString}</pre>
|
||||||
|
|||||||
@ -32,32 +32,6 @@
|
|||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyButton {
|
|
||||||
background: #007bff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: background 0.2s;
|
|
||||||
white-space: nowrap;
|
|
||||||
min-width: 110px;
|
|
||||||
height: 36px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #0056b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background: #004085;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.configContent {
|
.configContent {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
@ -100,10 +74,6 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyButton {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.jsonPre {
|
.jsonPre {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@ -3,11 +3,12 @@ import styles from './Note.module.scss';
|
|||||||
/**
|
/**
|
||||||
* Note component for displaying different types of messages
|
* Note component for displaying different types of messages
|
||||||
* @param {Object} props - Component properties
|
* @param {Object} props - Component properties
|
||||||
* @param {('note'|'warning'|'tip'|'important')} props.type - Type of note to display
|
* @param {('note'|'warning'|'tip'|'important'|'success')} props.type - Type of note to display
|
||||||
|
* @param {string} [props.title] - Optional custom title to override default type title
|
||||||
* @param {React.ReactNode} props.children - Content to display in the note
|
* @param {React.ReactNode} props.children - Content to display in the note
|
||||||
* @returns {JSX.Element} Note component
|
* @returns {JSX.Element} Note component
|
||||||
*/
|
*/
|
||||||
function Note({type = 'note', children}) {
|
function Note({type = 'note', title, children}) {
|
||||||
const typeConfig = {
|
const typeConfig = {
|
||||||
note: {
|
note: {
|
||||||
title: 'Note',
|
title: 'Note',
|
||||||
@ -61,16 +62,27 @@ function Note({type = 'note', children}) {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
title: 'Success',
|
||||||
|
className: styles.success,
|
||||||
|
icon: (
|
||||||
|
<svg className={styles.icon} width='16' height='16' viewBox='0 0 16 16' fill='none'>
|
||||||
|
<circle cx='8' cy='8' r='7.25' stroke='#007B14' strokeWidth='1.5' />
|
||||||
|
<path d='M4.66667 8L6.66667 10L11.3333 5.33333' stroke='#007B14' strokeWidth='1.5' strokeLinecap='round' strokeLinejoin='round' />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = typeConfig[type] || typeConfig.note;
|
const config = typeConfig[type] || typeConfig.note;
|
||||||
|
const displayTitle = title || config.title;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.noteContainer} ${config.className}`}>
|
<div className={`${styles.noteContainer} ${config.className}`}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
{config.icon}
|
{config.icon}
|
||||||
<span className={styles.title}>{config.title}</span>
|
<span className={styles.title}>{displayTitle}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.content}>{children}</div>
|
<div className={styles.content}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -99,3 +99,12 @@
|
|||||||
color: #262ba5;
|
color: #262ba5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Success variant - Green */
|
||||||
|
.success {
|
||||||
|
border-left: 2px solid #007b14;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #007b14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query';
|
import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query';
|
||||||
import Button from '../Button/Button';
|
import Button from '../Button/Button';
|
||||||
import Section from '../Section/Section';
|
import Section from '../Section/Section';
|
||||||
|
import Note from '../Note/Note';
|
||||||
import {enterMaintenanceMode, exitMaintenanceMode, getMaintenanceStatus} from '../../api';
|
import {enterMaintenanceMode, exitMaintenanceMode, getMaintenanceStatus} from '../../api';
|
||||||
import styles from './ShutdownTab.module.scss';
|
import styles from './ShutdownTab.module.scss';
|
||||||
|
|
||||||
@ -11,11 +12,15 @@ const SHUTDOWN_DESCRIPTION =
|
|||||||
const ShutdownTab = () => {
|
const ShutdownTab = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const {data: maintenanceStatus = {shutdown: false}, isLoading: statusLoading} = useQuery({
|
const {
|
||||||
|
data: maintenanceStatus,
|
||||||
|
isLoading: statusLoading,
|
||||||
|
isError: statusError,
|
||||||
|
error
|
||||||
|
} = useQuery({
|
||||||
queryKey: ['maintenanceStatus'],
|
queryKey: ['maintenanceStatus'],
|
||||||
queryFn: getMaintenanceStatus,
|
queryFn: getMaintenanceStatus,
|
||||||
retry: false,
|
retry: false
|
||||||
onError: () => {}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const shutdownMutation = useMutation({
|
const shutdownMutation = useMutation({
|
||||||
@ -56,22 +61,33 @@ const ShutdownTab = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statusError) {
|
||||||
|
return (
|
||||||
|
<Section title={SHUTDOWN_TITLE} description={SHUTDOWN_DESCRIPTION}>
|
||||||
|
<Note type='warning' title='Failed to Load Status'>
|
||||||
|
Unable to connect to DocService. {error?.message || 'Please check if the service is running.'}
|
||||||
|
</Note>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title={SHUTDOWN_TITLE} description={SHUTDOWN_DESCRIPTION}>
|
<Section title={SHUTDOWN_TITLE} description={SHUTDOWN_DESCRIPTION}>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={`${styles.statusBadge} ${maintenanceStatus.shutdown ? styles.statusBadgeWarning : styles.statusBadgeSuccess}`}>
|
<div className={styles.statusNote}>
|
||||||
<div className={styles.statusTitle}>Current Status:</div>
|
{maintenanceStatus?.shutdown ? (
|
||||||
<div className={styles.statusContent}>
|
<Note type='warning' title='Shutdown Mode Active'>
|
||||||
{maintenanceStatus.shutdown ? (
|
New connections are blocked and existing sessions are being closed.
|
||||||
<span className={styles.statusTextWarning}>⚠️ Shutdown Mode Active - New connections blocked</span>
|
</Note>
|
||||||
) : (
|
) : (
|
||||||
<span className={styles.statusTextSuccess}>✓ Normal Operation - Accepting new connections</span>
|
<Note type='success' title='Normal Operation'>
|
||||||
)}
|
Server is accepting new connections.
|
||||||
</div>
|
</Note>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleShutdown} disabled={maintenanceStatus.shutdown || shutdownMutation.isPending} disableResult={true}>
|
<Button onClick={handleShutdown} disabled={maintenanceStatus?.shutdown || shutdownMutation.isPending}>
|
||||||
Shutdown
|
Shutdown
|
||||||
</Button>
|
</Button>
|
||||||
<p className={styles.buttonDescription}>
|
<p className={styles.buttonDescription}>
|
||||||
@ -79,7 +95,7 @@ const ShutdownTab = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleResume} disabled={!maintenanceStatus.shutdown || resumeMutation.isPending}>
|
<Button onClick={handleResume} disabled={!maintenanceStatus?.shutdown || resumeMutation.isPending}>
|
||||||
Resume
|
Resume
|
||||||
</Button>
|
</Button>
|
||||||
<p className={styles.buttonDescription}>Returns server to normal mode and allows new editor connections.</p>
|
<p className={styles.buttonDescription}>Returns server to normal mode and allows new editor connections.</p>
|
||||||
|
|||||||
@ -10,39 +10,10 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statusBadge {
|
.statusNote {
|
||||||
padding: 12px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statusBadgeSuccess {
|
|
||||||
background-color: #d4edda;
|
|
||||||
border: 1px solid #28a745;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusBadgeWarning {
|
|
||||||
background-color: #fff3cd;
|
|
||||||
border: 1px solid #ffc107;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusTitle {
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusContent {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusTextSuccess {
|
|
||||||
color: #155724;
|
|
||||||
}
|
|
||||||
|
|
||||||
.statusTextWarning {
|
|
||||||
color: #856404;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttonDescription {
|
.buttonDescription {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
169
AdminPanel/server/sources/routes/docservice/router.js
Normal file
169
AdminPanel/server/sources/routes/docservice/router.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const http = require('http');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
const cookieParser = require('cookie-parser');
|
||||||
|
const operationContext = require('../../../../../Common/sources/operationContext');
|
||||||
|
const adminPanelJwtSecret = require('../../jwtSecret');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.use(express.json());
|
||||||
|
router.use(cookieParser());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware to verify JWT token
|
||||||
|
*/
|
||||||
|
function requireAuth(req, res, next) {
|
||||||
|
try {
|
||||||
|
const token = req.cookies?.accessToken;
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).json({error: 'Unauthorized'});
|
||||||
|
}
|
||||||
|
const decoded = jwt.verify(token, adminPanelJwtSecret);
|
||||||
|
req.user = decoded;
|
||||||
|
next();
|
||||||
|
} catch {
|
||||||
|
res.status(401).json({error: 'Unauthorized'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DocService connection config
|
||||||
|
*/
|
||||||
|
function getDocServiceConfig(ctx) {
|
||||||
|
const host = 'localhost';
|
||||||
|
const port = parseInt(ctx.getCfg('services.CoAuthoring.server.port', 8000), 10);
|
||||||
|
return {host, port};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make HTTP request to DocService
|
||||||
|
*/
|
||||||
|
function makeDocServiceRequest(options, ctx) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request(options, res => {
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
res.on('data', chunk => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const jsonData = data ? JSON.parse(data) : {};
|
||||||
|
resolve({
|
||||||
|
statusCode: res.statusCode,
|
||||||
|
headers: res.headers,
|
||||||
|
data: jsonData
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
ctx.logger.error('Error parsing DocService response: %s', err.stack);
|
||||||
|
reject(new Error('Invalid response from DocService'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', err => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.setTimeout(120000, () => {
|
||||||
|
req.destroy();
|
||||||
|
reject(new Error('Request timeout'));
|
||||||
|
});
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /shutdown - Get shutdown status
|
||||||
|
* Proxies to DocService GET /internal/cluster/inactive
|
||||||
|
*/
|
||||||
|
router.get('/shutdown', requireAuth, async (req, res) => {
|
||||||
|
const ctx = new operationContext.Context();
|
||||||
|
ctx.initFromRequest(req);
|
||||||
|
const {host, port} = getDocServiceConfig(ctx);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {
|
||||||
|
hostname: host,
|
||||||
|
port,
|
||||||
|
path: '/internal/cluster/inactive',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Forwarded-For': req.ip
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await makeDocServiceRequest(options, ctx);
|
||||||
|
res.status(response.statusCode).json(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
ctx.logger.error('Error getting shutdown status: %s', error.stack);
|
||||||
|
res.status(500).json({error: 'Failed to get shutdown status'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /shutdown - Enter shutdown mode
|
||||||
|
* Proxies to DocService PUT /internal/cluster/inactive
|
||||||
|
*/
|
||||||
|
router.put('/shutdown', requireAuth, async (req, res) => {
|
||||||
|
const ctx = new operationContext.Context();
|
||||||
|
ctx.initFromRequest(req);
|
||||||
|
const {host, port} = getDocServiceConfig(ctx);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.logger.info('Entering shutdown mode via AdminPanel');
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
hostname: host,
|
||||||
|
port,
|
||||||
|
path: '/internal/cluster/inactive',
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-Forwarded-For': req.ip
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await makeDocServiceRequest(options, ctx);
|
||||||
|
res.status(response.statusCode).json(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
ctx.logger.error('Error entering shutdown mode: %s', error.stack);
|
||||||
|
res.status(500).json({error: 'Failed to enter shutdown mode'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE /shutdown - Exit shutdown mode
|
||||||
|
* Proxies to DocService DELETE /internal/cluster/inactive
|
||||||
|
*/
|
||||||
|
router.delete('/shutdown', requireAuth, async (req, res) => {
|
||||||
|
const ctx = new operationContext.Context();
|
||||||
|
ctx.initFromRequest(req);
|
||||||
|
const {host, port} = getDocServiceConfig(ctx);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.logger.info('Exiting shutdown mode via AdminPanel');
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
hostname: host,
|
||||||
|
port,
|
||||||
|
path: '/internal/cluster/inactive',
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'X-Forwarded-For': req.ip
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await makeDocServiceRequest(options, ctx);
|
||||||
|
res.status(response.statusCode).json(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
ctx.logger.error('Error exiting shutdown mode: %s', error.stack);
|
||||||
|
res.status(500).json({error: 'Failed to exit shutdown mode'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@ -49,6 +49,7 @@ const infoRouter = require('../../../DocService/sources/routes/info');
|
|||||||
const configRouter = require('./routes/config/router');
|
const configRouter = require('./routes/config/router');
|
||||||
const adminpanelRouter = require('./routes/adminpanel/router');
|
const adminpanelRouter = require('./routes/adminpanel/router');
|
||||||
const wopiRouter = require('./routes/wopi/router');
|
const wopiRouter = require('./routes/wopi/router');
|
||||||
|
const docserviceRouter = require('./routes/docservice/router');
|
||||||
const passwordManager = require('./passwordManager');
|
const passwordManager = require('./passwordManager');
|
||||||
const bootstrap = require('./bootstrap');
|
const bootstrap = require('./bootstrap');
|
||||||
const devProxy = require('./devProxy');
|
const devProxy = require('./devProxy');
|
||||||
@ -131,6 +132,7 @@ function disableCache(req, res, next) {
|
|||||||
// API routes under /admin prefix
|
// API routes under /admin prefix
|
||||||
app.use('/admin/api/v1/config', disableCache, configRouter);
|
app.use('/admin/api/v1/config', disableCache, configRouter);
|
||||||
app.use('/admin/api/v1/wopi', disableCache, wopiRouter);
|
app.use('/admin/api/v1/wopi', disableCache, wopiRouter);
|
||||||
|
app.use('/admin/api/v1/docservice', disableCache, docserviceRouter);
|
||||||
app.use('/admin/api/v1', disableCache, adminpanelRouter);
|
app.use('/admin/api/v1', disableCache, adminpanelRouter);
|
||||||
app.get('/admin/api/v1/stat', disableCache, async (req, res) => {
|
app.get('/admin/api/v1/stat', disableCache, async (req, res) => {
|
||||||
await infoRouter.licenseInfo(req, res);
|
await infoRouter.licenseInfo(req, res);
|
||||||
|
|||||||
Reference in New Issue
Block a user