mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
[feature] Move info.json request to common router
This commit is contained in:
@ -6,7 +6,7 @@ import Button from '../../components/Button';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function Login() {
|
||||
const [tenantName, setTenantName] = useState('');
|
||||
const [tenantName, setTenantName] = useState('localhost');
|
||||
const [secret, setSecret] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@ -22,5 +22,11 @@
|
||||
"joi": "^17.13.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"pkg": {
|
||||
"scripts": [
|
||||
"../../DocService/sources/editorDataMemory.js",
|
||||
"../../DocService/sources/editorDataRedis.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,12 +38,12 @@ const operationContext = require('../../../Common/sources/operationContext');
|
||||
const tenantManager = require('../../../Common/sources/tenantManager');
|
||||
const license = require('../../../Common/sources/license');
|
||||
const utils = require('../../../Common/sources/utils');
|
||||
const commonDefines = require('../../../Common/sources/commondefines');
|
||||
|
||||
const express = require('express');
|
||||
const http = require('http');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const infoRouter = require('../../../DocService/sources/routes/info');
|
||||
|
||||
const configRouter = require('./routes/config/router');
|
||||
const adminpanelRouter = require('./routes/adminpanel/router');
|
||||
@ -78,38 +78,10 @@ const corsWithCredentials = cors({
|
||||
|
||||
operationContext.global.logger.warn('AdminPanel server starting...');
|
||||
|
||||
app.get('/info/info.json', cors(), utils.checkClientIp, async (req, res) => {
|
||||
const serverDate = new Date();
|
||||
serverDate.setMilliseconds(0);
|
||||
const output = {
|
||||
connectionsStat: {},
|
||||
licenseInfo: {},
|
||||
serverInfo: {
|
||||
buildVersion: commonDefines.buildVersion,
|
||||
buildNumber: commonDefines.buildNumber,
|
||||
date: serverDate.toISOString()
|
||||
},
|
||||
quota: {
|
||||
edit: {connectionsCount: 0, usersCount: {unique: 0, anonymous: 0}},
|
||||
view: {connectionsCount: 0, usersCount: {unique: 0, anonymous: 0}},
|
||||
byMonth: []
|
||||
}
|
||||
};
|
||||
const ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
await ctx.initTenantCache();
|
||||
const [licenseInfo] = await tenantManager.getTenantLicense(ctx);
|
||||
output.licenseInfo = licenseInfo || {};
|
||||
} catch (e) {
|
||||
ctx.logger && ctx.logger.warn('info.json error: %s', e.stack);
|
||||
} finally {
|
||||
res.json(output);
|
||||
}
|
||||
});
|
||||
|
||||
app.use('/info/config', corsWithCredentials, utils.checkClientIp, configRouter);
|
||||
app.use('/info/adminpanel', corsWithCredentials, utils.checkClientIp, adminpanelRouter);
|
||||
// Shared Info router (provides /info.json)
|
||||
app.use('/info', infoRouter());
|
||||
|
||||
// todo config or _dirname. Serve AdminPanel client build as static assets
|
||||
const clientBuildPath = path.resolve('client/build');
|
||||
|
||||
@ -4416,174 +4416,6 @@ exports.healthCheck = function (req, res) {
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.licenseInfo = function (req, res) {
|
||||
return co(function* () {
|
||||
let isError = false;
|
||||
const serverDate = new Date();
|
||||
//security risk of high-precision time
|
||||
serverDate.setMilliseconds(0);
|
||||
const output = {
|
||||
connectionsStat: {},
|
||||
licenseInfo: {},
|
||||
serverInfo: {
|
||||
buildVersion: commonDefines.buildVersion,
|
||||
buildNumber: commonDefines.buildNumber,
|
||||
date: serverDate.toISOString()
|
||||
},
|
||||
quota: {
|
||||
edit: {
|
||||
connectionsCount: 0,
|
||||
usersCount: {
|
||||
unique: 0,
|
||||
anonymous: 0
|
||||
}
|
||||
},
|
||||
view: {
|
||||
connectionsCount: 0,
|
||||
usersCount: {
|
||||
unique: 0,
|
||||
anonymous: 0
|
||||
}
|
||||
},
|
||||
byMonth: []
|
||||
}
|
||||
};
|
||||
|
||||
const ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
yield ctx.initTenantCache();
|
||||
ctx.logger.debug('licenseInfo start');
|
||||
|
||||
const [licenseInfo] = yield tenantManager.getTenantLicense(ctx);
|
||||
Object.assign(output.licenseInfo, licenseInfo);
|
||||
|
||||
const precisionSum = {};
|
||||
for (let i = 0; i < PRECISION.length; ++i) {
|
||||
precisionSum[PRECISION[i].name] = {
|
||||
edit: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0},
|
||||
liveview: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0},
|
||||
view: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0}
|
||||
};
|
||||
output.connectionsStat[PRECISION[i].name] = {
|
||||
edit: {min: 0, avr: 0, max: 0},
|
||||
liveview: {min: 0, avr: 0, max: 0},
|
||||
view: {min: 0, avr: 0, max: 0}
|
||||
};
|
||||
}
|
||||
const redisRes = yield editorStat.getEditorConnections(ctx);
|
||||
const now = Date.now();
|
||||
if (redisRes.length > 0) {
|
||||
const expDocumentsStep95 = expDocumentsStep * 0.95;
|
||||
let precisionIndex = 0;
|
||||
for (let i = redisRes.length - 1; i >= 0; i--) {
|
||||
const elem = redisRes[i];
|
||||
let edit = elem.edit || 0;
|
||||
let view = elem.view || 0;
|
||||
let liveview = elem.liveview || 0;
|
||||
//for cluster
|
||||
while (i > 0 && elem.time - redisRes[i - 1].time < expDocumentsStep95) {
|
||||
edit += elem.edit || 0;
|
||||
view += elem.view || 0;
|
||||
liveview += elem.liveview || 0;
|
||||
i--;
|
||||
}
|
||||
for (let j = precisionIndex; j < PRECISION.length; ++j) {
|
||||
if (now - elem.time < PRECISION[j].val) {
|
||||
const precision = precisionSum[PRECISION[j].name];
|
||||
precision.edit.min = Math.min(precision.edit.min, edit);
|
||||
precision.edit.max = Math.max(precision.edit.max, edit);
|
||||
precision.edit.sum += edit;
|
||||
precision.edit.count++;
|
||||
precision.view.min = Math.min(precision.view.min, view);
|
||||
precision.view.max = Math.max(precision.view.max, view);
|
||||
precision.view.sum += view;
|
||||
precision.view.count++;
|
||||
precision.liveview.min = Math.min(precision.liveview.min, liveview);
|
||||
precision.liveview.max = Math.max(precision.liveview.max, liveview);
|
||||
precision.liveview.sum += liveview;
|
||||
precision.liveview.count++;
|
||||
} else {
|
||||
precisionIndex = j + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const i in precisionSum) {
|
||||
const precision = precisionSum[i];
|
||||
const precisionOut = output.connectionsStat[i];
|
||||
if (precision.edit.count > 0) {
|
||||
precisionOut.edit.avr = Math.round(precision.edit.sum / precision.edit.intervalsInPresision);
|
||||
precisionOut.edit.min = precision.edit.min;
|
||||
precisionOut.edit.max = precision.edit.max;
|
||||
}
|
||||
if (precision.liveview.count > 0) {
|
||||
precisionOut.liveview.avr = Math.round(precision.liveview.sum / precision.liveview.intervalsInPresision);
|
||||
precisionOut.liveview.min = precision.liveview.min;
|
||||
precisionOut.liveview.max = precision.liveview.max;
|
||||
}
|
||||
if (precision.view.count > 0) {
|
||||
precisionOut.view.avr = Math.round(precision.view.sum / precision.view.intervalsInPresision);
|
||||
precisionOut.view.min = precision.view.min;
|
||||
precisionOut.view.max = precision.view.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
const nowUTC = getLicenseNowUtc();
|
||||
let execRes;
|
||||
execRes = yield editorStat.getPresenceUniqueUser(ctx, nowUTC);
|
||||
output.quota.edit.connectionsCount = yield editorStat.getEditorConnectionsCount(ctx, connections);
|
||||
output.quota.edit.usersCount.unique = execRes.length;
|
||||
execRes.forEach(elem => {
|
||||
if (elem.anonym) {
|
||||
output.quota.edit.usersCount.anonymous++;
|
||||
}
|
||||
});
|
||||
|
||||
execRes = yield editorStat.getPresenceUniqueViewUser(ctx, nowUTC);
|
||||
output.quota.view.connectionsCount = yield editorStat.getLiveViewerConnectionsCount(ctx, connections);
|
||||
output.quota.view.usersCount.unique = execRes.length;
|
||||
execRes.forEach(elem => {
|
||||
if (elem.anonym) {
|
||||
output.quota.view.usersCount.anonymous++;
|
||||
}
|
||||
});
|
||||
|
||||
const byMonth = yield editorStat.getPresenceUniqueUsersOfMonth(ctx);
|
||||
const byMonthView = yield editorStat.getPresenceUniqueViewUsersOfMonth(ctx);
|
||||
const byMonthMerged = [];
|
||||
for (const i in byMonth) {
|
||||
if (Object.hasOwn(byMonth, i)) {
|
||||
byMonthMerged[i] = {date: i, users: byMonth[i], usersView: {}};
|
||||
}
|
||||
}
|
||||
for (const i in byMonthView) {
|
||||
if (Object.hasOwn(byMonthView, i)) {
|
||||
if (Object.hasOwn(byMonthMerged, i)) {
|
||||
byMonthMerged[i].usersView = byMonthView[i];
|
||||
} else {
|
||||
byMonthMerged[i] = {date: i, users: {}, usersView: byMonthView[i]};
|
||||
}
|
||||
}
|
||||
}
|
||||
output.quota.byMonth = Object.values(byMonthMerged);
|
||||
output.quota.byMonth.sort((a, b) => {
|
||||
return a.date.localeCompare(b.date);
|
||||
});
|
||||
|
||||
ctx.logger.debug('licenseInfo end');
|
||||
} catch (err) {
|
||||
isError = true;
|
||||
ctx.logger.error('licenseInfo error %s', err.stack);
|
||||
} finally {
|
||||
if (!isError) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify(output));
|
||||
} else {
|
||||
res.sendStatus(400);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
function validateInputParams(ctx, authRes, command) {
|
||||
const commandsWithoutKey = ['version', 'license', 'getForgottenList'];
|
||||
const isValidWithoutKey = commandsWithoutKey.includes(command.c);
|
||||
|
||||
244
DocService/sources/routes/info.js
Normal file
244
DocService/sources/routes/info.js
Normal file
@ -0,0 +1,244 @@
|
||||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const ms = require('ms');
|
||||
const config = require('config');
|
||||
const cron = require('cron');
|
||||
const utils = require('../../../Common/sources/utils');
|
||||
const commonDefines = require('../../../Common/sources/commondefines');
|
||||
const operationContext = require('../../../Common/sources/operationContext');
|
||||
const tenantManager = require('../../../Common/sources/tenantManager');
|
||||
|
||||
// Configuration values
|
||||
const cfgExpDocumentsCron = config.get('services.CoAuthoring.expire.documentsCron');
|
||||
const cfgEditorStatStorage =
|
||||
config.get('services.CoAuthoring.server.editorStatStorage') || config.get('services.CoAuthoring.server.editorDataStorage');
|
||||
|
||||
// Initialize editor stat storage
|
||||
const editorStatStorage = require(`../${cfgEditorStatStorage}`);
|
||||
const editorStat = new editorStatStorage.EditorStat();
|
||||
console.error(`../${cfgEditorStatStorage}`);
|
||||
console.error(editorStat);
|
||||
// Constants
|
||||
const PRECISION = [
|
||||
{name: 'hour', val: ms('1h')},
|
||||
{name: 'day', val: ms('1d')},
|
||||
{name: 'week', val: ms('7d')},
|
||||
{name: 'month', val: ms('30d')},
|
||||
{name: 'year', val: ms('365d')}
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the time step in milliseconds between cron job executions
|
||||
* @param {string} cronTime - Cron time expression
|
||||
* @returns {number} Time difference in milliseconds between consecutive executions
|
||||
*/
|
||||
function getCronStep(cronTime) {
|
||||
const cronJob = new cron.CronJob(cronTime, () => {});
|
||||
const dates = cronJob.nextDates(2);
|
||||
return dates[1] - dates[0];
|
||||
}
|
||||
|
||||
const expDocumentsStep = getCronStep(cfgExpDocumentsCron);
|
||||
|
||||
/**
|
||||
* Get current UTC timestamp for license calculations
|
||||
* @returns {number} UTC timestamp in seconds
|
||||
*/
|
||||
function getLicenseNowUtc() {
|
||||
const now = new Date();
|
||||
return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds()) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* License info endpoint handler
|
||||
* @param {import('express').Request} req Express request
|
||||
* @param {import('express').Response} res Express response
|
||||
*/
|
||||
async function licenseInfo(req, res) {
|
||||
let isError = false;
|
||||
const serverDate = new Date();
|
||||
// Security risk of high-precision time
|
||||
serverDate.setMilliseconds(0);
|
||||
const output = {
|
||||
connectionsStat: {},
|
||||
licenseInfo: {},
|
||||
serverInfo: {
|
||||
buildVersion: commonDefines.buildVersion,
|
||||
buildNumber: commonDefines.buildNumber,
|
||||
date: serverDate.toISOString()
|
||||
},
|
||||
quota: {
|
||||
edit: {
|
||||
connectionsCount: 0,
|
||||
usersCount: {
|
||||
unique: 0,
|
||||
anonymous: 0
|
||||
}
|
||||
},
|
||||
view: {
|
||||
connectionsCount: 0,
|
||||
usersCount: {
|
||||
unique: 0,
|
||||
anonymous: 0
|
||||
}
|
||||
},
|
||||
byMonth: []
|
||||
}
|
||||
};
|
||||
|
||||
const ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
await ctx.initTenantCache();
|
||||
ctx.logger.debug('licenseInfo start');
|
||||
|
||||
const tenantLicense = await tenantManager.getTenantLicense(ctx);
|
||||
if (tenantLicense && Array.isArray(tenantLicense) && tenantLicense.length > 0) {
|
||||
const [licenseInfo] = tenantLicense;
|
||||
Object.assign(output.licenseInfo, licenseInfo);
|
||||
}
|
||||
|
||||
const precisionSum = {};
|
||||
for (let i = 0; i < PRECISION.length; ++i) {
|
||||
precisionSum[PRECISION[i].name] = {
|
||||
edit: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0},
|
||||
liveview: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0},
|
||||
view: {min: Number.MAX_VALUE, sum: 0, count: 0, intervalsInPresision: PRECISION[i].val / expDocumentsStep, max: 0}
|
||||
};
|
||||
output.connectionsStat[PRECISION[i].name] = {
|
||||
edit: {min: 0, avr: 0, max: 0},
|
||||
liveview: {min: 0, avr: 0, max: 0},
|
||||
view: {min: 0, avr: 0, max: 0}
|
||||
};
|
||||
}
|
||||
|
||||
const redisRes = await editorStat.getEditorConnections(ctx);
|
||||
const now = Date.now();
|
||||
if (redisRes.length > 0) {
|
||||
const expDocumentsStep95 = expDocumentsStep * 0.95;
|
||||
let precisionIndex = 0;
|
||||
for (let i = redisRes.length - 1; i >= 0; i--) {
|
||||
const elem = redisRes[i];
|
||||
let edit = elem.edit || 0;
|
||||
let view = elem.view || 0;
|
||||
let liveview = elem.liveview || 0;
|
||||
// For cluster
|
||||
while (i > 0 && elem.time - redisRes[i - 1].time < expDocumentsStep95) {
|
||||
edit += elem.edit || 0;
|
||||
view += elem.view || 0;
|
||||
liveview += elem.liveview || 0;
|
||||
i--;
|
||||
}
|
||||
for (let j = precisionIndex; j < PRECISION.length; ++j) {
|
||||
if (now - elem.time < PRECISION[j].val) {
|
||||
const precision = precisionSum[PRECISION[j].name];
|
||||
precision.edit.min = Math.min(precision.edit.min, edit);
|
||||
precision.edit.max = Math.max(precision.edit.max, edit);
|
||||
precision.edit.sum += edit;
|
||||
precision.edit.count++;
|
||||
precision.view.min = Math.min(precision.view.min, view);
|
||||
precision.view.max = Math.max(precision.view.max, view);
|
||||
precision.view.sum += view;
|
||||
precision.view.count++;
|
||||
precision.liveview.min = Math.min(precision.liveview.min, liveview);
|
||||
precision.liveview.max = Math.max(precision.liveview.max, liveview);
|
||||
precision.liveview.sum += liveview;
|
||||
precision.liveview.count++;
|
||||
} else {
|
||||
precisionIndex = j + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const i in precisionSum) {
|
||||
const precision = precisionSum[i];
|
||||
const precisionOut = output.connectionsStat[i];
|
||||
if (precision.edit.count > 0) {
|
||||
precisionOut.edit.avr = Math.round(precision.edit.sum / precision.edit.intervalsInPresision);
|
||||
precisionOut.edit.min = precision.edit.min;
|
||||
precisionOut.edit.max = precision.edit.max;
|
||||
}
|
||||
if (precision.liveview.count > 0) {
|
||||
precisionOut.liveview.avr = Math.round(precision.liveview.sum / precision.liveview.intervalsInPresision);
|
||||
precisionOut.liveview.min = precision.liveview.min;
|
||||
precisionOut.liveview.max = precision.liveview.max;
|
||||
}
|
||||
if (precision.view.count > 0) {
|
||||
precisionOut.view.avr = Math.round(precision.view.sum / precision.view.intervalsInPresision);
|
||||
precisionOut.view.min = precision.view.min;
|
||||
precisionOut.view.max = precision.view.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nowUTC = getLicenseNowUtc();
|
||||
let execRes;
|
||||
execRes = await editorStat.getPresenceUniqueUser(ctx, nowUTC);
|
||||
output.quota.edit.connectionsCount = await editorStat.getEditorConnectionsCount(ctx, {});
|
||||
output.quota.edit.usersCount.unique = execRes.length;
|
||||
execRes.forEach(elem => {
|
||||
if (elem.anonym) {
|
||||
output.quota.edit.usersCount.anonymous++;
|
||||
}
|
||||
});
|
||||
|
||||
execRes = await editorStat.getPresenceUniqueViewUser(ctx, nowUTC);
|
||||
output.quota.view.connectionsCount = await editorStat.getLiveViewerConnectionsCount(ctx, {});
|
||||
output.quota.view.usersCount.unique = execRes.length;
|
||||
execRes.forEach(elem => {
|
||||
if (elem.anonym) {
|
||||
output.quota.view.usersCount.anonymous++;
|
||||
}
|
||||
});
|
||||
|
||||
const byMonth = await editorStat.getPresenceUniqueUsersOfMonth(ctx);
|
||||
const byMonthView = await editorStat.getPresenceUniqueViewUsersOfMonth(ctx);
|
||||
const byMonthMerged = [];
|
||||
for (const i in byMonth) {
|
||||
if (Object.hasOwn(byMonth, i)) {
|
||||
byMonthMerged[i] = {date: i, users: byMonth[i], usersView: {}};
|
||||
}
|
||||
}
|
||||
for (const i in byMonthView) {
|
||||
if (Object.hasOwn(byMonthView, i)) {
|
||||
if (Object.hasOwn(byMonthMerged, i)) {
|
||||
byMonthMerged[i].usersView = byMonthView[i];
|
||||
} else {
|
||||
byMonthMerged[i] = {date: i, users: {}, usersView: byMonthView[i]};
|
||||
}
|
||||
}
|
||||
}
|
||||
output.quota.byMonth = Object.values(byMonthMerged);
|
||||
output.quota.byMonth.sort((a, b) => {
|
||||
return a.date.localeCompare(b.date);
|
||||
});
|
||||
|
||||
ctx.logger.debug('licenseInfo end');
|
||||
} catch (err) {
|
||||
isError = true;
|
||||
ctx.logger.error('licenseInfo error %s', err.stack);
|
||||
} finally {
|
||||
if (!isError) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.send(JSON.stringify(output));
|
||||
} else {
|
||||
res.sendStatus(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create shared Info router
|
||||
* @returns {import('express').Router} Router instance
|
||||
*/
|
||||
function createInfoRouter() {
|
||||
const router = express.Router();
|
||||
|
||||
// License info endpoint with CORS and client IP check
|
||||
router.get('/info.json', cors(), utils.checkClientIp, licenseInfo);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
module.exports = createInfoRouter;
|
||||
@ -59,6 +59,7 @@ const tenantManager = require('./../../Common/sources/tenantManager');
|
||||
const staticRouter = require('./routes/static');
|
||||
const configRouter = require('./routes/config');
|
||||
const adminpanelRouter = require('./routes/adminpanel/router');
|
||||
const infoRouter = require('./routes/info');
|
||||
const ms = require('ms');
|
||||
const aiProxyHandler = require('./ai/aiProxyHandler');
|
||||
const cors = require('cors');
|
||||
@ -263,11 +264,12 @@ docsCoServer.install(server, () => {
|
||||
app.post('/docbuilder', utils.checkClientIp, rawFileParser, (req, res) => {
|
||||
converterService.builder(req, res);
|
||||
});
|
||||
app.get('/info/info.json', cors(), utils.checkClientIp, docsCoServer.licenseInfo);
|
||||
app.use('/info/config', corsWithCredentials, utils.checkClientIp, configRouter);
|
||||
app.use('/info/adminpanel', corsWithCredentials, utils.checkClientIp, adminpanelRouter);
|
||||
app.get('/info/plugin/settings', utils.checkClientIp, aiProxyHandler.requestSettings);
|
||||
app.post('/info/plugin/models', utils.checkClientIp, rawFileParser, aiProxyHandler.requestModels);
|
||||
// Shared Info router (provides /info.json)
|
||||
app.use('/info', infoRouter());
|
||||
app.put('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);
|
||||
app.delete('/internal/cluster/inactive', utils.checkClientIp, docsCoServer.shutdown);
|
||||
app.get('/internal/connections/edit', docsCoServer.getEditorConnectionsCount);
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
"install:FileConverter": "npm ci --prefix ./FileConverter",
|
||||
"install:Metrics": "npm ci --prefix ./Metrics",
|
||||
"install:AdminPanel/server": "npm ci --prefix ./AdminPanel/server",
|
||||
"install:AdminPanel/client": "npm ci --prefix ./AdminPanel/client",
|
||||
"install:AdminPanel/client": "npm ci --prefix ./AdminPanel/client && npm --prefix ./AdminPanel/client run build",
|
||||
"3d-party-lic-json:Common": "license-report --output=json --package=./Common/package.json --config ./3d-party-lic-report/license-report-config.json > ./3d-party-lic-report/license-report.json",
|
||||
"3d-party-lic-json:DocService": "license-report --output=json --package=./DocService/package.json --config ./3d-party-lic-report/license-report-config.json > ./3d-party-lic-report/license-report.json",
|
||||
"3d-party-lic-json:FileConverter": "license-report --output=json --package=./FileConverter/package.json --config ./3d-party-lic-report/license-report-config.json > ./3d-party-lic-report/license-report.json",
|
||||
|
||||
Reference in New Issue
Block a user