mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
[feature] License expiration notification changed
This commit is contained in:
@ -37,13 +37,13 @@
|
|||||||
"notification": {
|
"notification": {
|
||||||
"enable": false,
|
"enable": false,
|
||||||
"rules": {
|
"rules": {
|
||||||
"licenseExpired": {
|
"licenseExpirationWarning": {
|
||||||
"transportType": [
|
"transportType": [
|
||||||
"email"
|
"email"
|
||||||
],
|
],
|
||||||
"template": {
|
"template": {
|
||||||
"title": "License",
|
"title": "License expiration",
|
||||||
"body": "%s license expires in %s!!!"
|
"body": "%s license %s on %s!!!"
|
||||||
},
|
},
|
||||||
"policies": {
|
"policies": {
|
||||||
"repeatInterval": "1d"
|
"repeatInterval": "1d"
|
||||||
@ -426,7 +426,7 @@
|
|||||||
"license_file": "",
|
"license_file": "",
|
||||||
"warning_limit_percents": 70,
|
"warning_limit_percents": 70,
|
||||||
"packageType": 0,
|
"packageType": 0,
|
||||||
"startNotifyFrom": "30d"
|
"warning_license_expiration": "30d"
|
||||||
},
|
},
|
||||||
"FileConverter": {
|
"FileConverter": {
|
||||||
"converter": {
|
"converter": {
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
5
Common/npm-shrinkwrap.json
generated
5
Common/npm-shrinkwrap.json
generated
@ -1986,11 +1986,6 @@
|
|||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"uuid": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
|
|
||||||
},
|
|
||||||
"verror": {
|
"verror": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||||
|
|||||||
@ -18,14 +18,13 @@
|
|||||||
"log4js": "6.4.1",
|
"log4js": "6.4.1",
|
||||||
"mime": "2.3.1",
|
"mime": "2.3.1",
|
||||||
"ms": "2.1.1",
|
"ms": "2.1.1",
|
||||||
"nodemailer": "6.9.13",
|
|
||||||
"node-cache": "4.2.1",
|
"node-cache": "4.2.1",
|
||||||
"node-statsd": "0.1.1",
|
"node-statsd": "0.1.1",
|
||||||
|
"nodemailer": "6.9.13",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-filtering-agent": "1.0.5",
|
"request-filtering-agent": "1.0.5",
|
||||||
"rhea": "1.0.24",
|
"rhea": "1.0.24",
|
||||||
"uri-js": "4.2.2",
|
"uri-js": "4.2.2",
|
||||||
"uuid": "9.0.1",
|
|
||||||
"win-ca": "3.5.0"
|
"win-ca": "3.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@ exports.readLicense = async function () {
|
|||||||
plugins: false,
|
plugins: false,
|
||||||
buildDate: oBuildDate,
|
buildDate: oBuildDate,
|
||||||
startDate: startDate,
|
startDate: startDate,
|
||||||
endDate: '2024-06-29T04:13:00.000Z',
|
endDate: null,
|
||||||
customerId: "",
|
customerId: "",
|
||||||
alias: ""
|
alias: ""
|
||||||
}, null];
|
}, null];
|
||||||
|
|||||||
@ -41,11 +41,11 @@ const cfgMailServer = config.get('email.smtpServerConfiguration');
|
|||||||
const cfgMailMessageDefaults = config.get('email.contactDefaults');
|
const cfgMailMessageDefaults = config.get('email.contactDefaults');
|
||||||
const cfgNotificationEnable = config.get('notification.enable');
|
const cfgNotificationEnable = config.get('notification.enable');
|
||||||
|
|
||||||
const defaultRepeatInterval = 1000 * 60 * 60 * 24;
|
const infiniteRepeatInterval = Infinity;
|
||||||
const repeatIntervalsExpired = new Map();
|
const repeatIntervalsExpired = new Map();
|
||||||
const notificationTypes = {
|
const notificationTypes = {
|
||||||
LICENSE_EXPIRED: "licenseExpired",
|
LICENSE_EXPIRATION_WARNING: 'licenseExpirationWarning',
|
||||||
LICENSE_LIMIT: "licenseLimit"
|
LICENSE_LIMIT: 'licenseLimit'
|
||||||
};
|
};
|
||||||
|
|
||||||
class TransportInterface {
|
class TransportInterface {
|
||||||
@ -118,7 +118,7 @@ async function notify(ctx, notificationType, messageParams) {
|
|||||||
|
|
||||||
function checkRulePolicies(ctx, notificationType, tenRule) {
|
function checkRulePolicies(ctx, notificationType, tenRule) {
|
||||||
const { repeatInterval } = tenRule.policies;
|
const { repeatInterval } = tenRule.policies;
|
||||||
const intervalMilliseconds = ms(repeatInterval) ?? defaultRepeatInterval;
|
const intervalMilliseconds = repeatInterval ? ms(repeatInterval) : infiniteRepeatInterval;
|
||||||
const cacheKey = `${notificationType}_${ctx.tenant}`;
|
const cacheKey = `${notificationType}_${ctx.tenant}`;
|
||||||
const expired = repeatIntervalsExpired.get(cacheKey);
|
const expired = repeatIntervalsExpired.get(cacheKey);
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ function checkRulePolicies(ctx, notificationType, tenRule) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.logger.debug(`Notification service: skip rule "%s" due to repeat interval %s`, notificationType, repeatInterval);
|
ctx.logger.debug(`Notification service: skip rule "%s" due to repeat interval = %s`, notificationType, repeatInterval ?? "infinite");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,7 +108,6 @@ const cfgEditorDataStorage = config.get('services.CoAuthoring.server.editorDataS
|
|||||||
const cfgEditorStatStorage = config.get('services.CoAuthoring.server.editorStatStorage');
|
const cfgEditorStatStorage = config.get('services.CoAuthoring.server.editorStatStorage');
|
||||||
const editorDataStorage = require('./' + cfgEditorDataStorage);
|
const editorDataStorage = require('./' + cfgEditorDataStorage);
|
||||||
const editorStatStorage = require('./' + (cfgEditorStatStorage || cfgEditorDataStorage));
|
const editorStatStorage = require('./' + (cfgEditorStatStorage || cfgEditorDataStorage));
|
||||||
const utilsDocService = require("./utilsDocService");
|
|
||||||
|
|
||||||
const cfgEditSingleton = config.get('services.CoAuthoring.server.edit_singleton');
|
const cfgEditSingleton = config.get('services.CoAuthoring.server.edit_singleton');
|
||||||
const cfgEditor = config.get('services.CoAuthoring.editor');
|
const cfgEditor = config.get('services.CoAuthoring.editor');
|
||||||
|
|||||||
@ -38,11 +38,10 @@ const Jimp = require('jimp');
|
|||||||
const locale = require('windows-locale');
|
const locale = require('windows-locale');
|
||||||
const ms = require('ms');
|
const ms = require('ms');
|
||||||
|
|
||||||
const utils = require('../../Common/sources/utils');
|
|
||||||
const tenantManager = require('../../Common/sources/tenantManager');
|
const tenantManager = require('../../Common/sources/tenantManager');
|
||||||
const { notificationTypes, ...notificationService } = require('../../Common/sources/notificationService');
|
const { notificationTypes, ...notificationService } = require('../../Common/sources/notificationService');
|
||||||
|
|
||||||
const cfgStartNotifyFrom = ms(config.get('license.startNotifyFrom'));
|
const cfgStartNotifyFrom = ms(config.get('license.warning_license_expiration'));
|
||||||
|
|
||||||
async function fixImageExifRotation(ctx, buffer) {
|
async function fixImageExifRotation(ctx, buffer) {
|
||||||
if (!buffer) {
|
if (!buffer) {
|
||||||
@ -78,66 +77,29 @@ function localeToLCID(lang) {
|
|||||||
return elem && elem.id;
|
return elem && elem.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanFriendlyExpirationTime(ctx, endTime) {
|
function humanFriendlyExpirationTime(endTime) {
|
||||||
const timeWithPostfix = (timeName, value) => `${value} ${timeName}${value > 1 ? 's' : ''}`;
|
const month = [
|
||||||
const currentTime = new Date();
|
'January',
|
||||||
const oneMinute = 1000 * 60;
|
'February',
|
||||||
const oneHour = oneMinute * 60;
|
'March',
|
||||||
const oneDay = oneHour * 24;
|
'April',
|
||||||
const absoluteDiff = endTime.getTime() - currentTime.getTime();
|
'May',
|
||||||
|
'June',
|
||||||
|
'July',
|
||||||
|
'August',
|
||||||
|
'September',
|
||||||
|
'October',
|
||||||
|
'November',
|
||||||
|
'December'
|
||||||
|
];
|
||||||
|
|
||||||
currentTime.setUTCSeconds(0,0);
|
return `${month[endTime.getUTCMonth()]} ${endTime.getUTCDate()}, ${endTime.getUTCFullYear()}`
|
||||||
|
|
||||||
if (endTime.getTime() < currentTime.getTime()) {
|
|
||||||
ctx.logger.warn(`humanFriendlyExpirationTime(): expiration date value is lesser than current date`);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const floatResult = absoluteDiff / oneDay;
|
|
||||||
const daysCount = floatResult < 1 ? 0 : Math.round(floatResult);
|
|
||||||
const monthDiff = utils.getMonthDiff(currentTime, endTime);
|
|
||||||
if (monthDiff >= 1 && daysCount >= currentTime.getDaysInMonth()) {
|
|
||||||
return timeWithPostfix('month', monthDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (daysCount > 0) {
|
|
||||||
return timeWithPostfix('day', daysCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This time we cannot just round division operation to the nearest digit because we need minutes value and more accuracy.
|
|
||||||
let hoursCount = 0
|
|
||||||
for (; hoursCount * oneHour <= absoluteDiff; hoursCount++) {}
|
|
||||||
|
|
||||||
if (hoursCount * oneHour > absoluteDiff) {
|
|
||||||
hoursCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
let minutesCount = Math.round((absoluteDiff - hoursCount * oneHour) / oneMinute);
|
|
||||||
if(minutesCount >= 60) {
|
|
||||||
hoursCount++;
|
|
||||||
minutesCount -= 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeString = '';
|
|
||||||
if (hoursCount > 0) {
|
|
||||||
timeString += timeWithPostfix('hour', hoursCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minutesCount > 0) {
|
|
||||||
if (timeString.length !== 0) {
|
|
||||||
timeString += ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
timeString += timeWithPostfix('minute', minutesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify server user about license expiration via configured notification transports.
|
* Notify server user about license expiration via configured notification transports.
|
||||||
* @param {string} ctx Context.
|
* @param {string} ctx Context.
|
||||||
* @param {date} endDate Date of expiration.
|
* @param {Date} endDate Date of expiration.
|
||||||
* @returns {undefined}
|
* @returns {undefined}
|
||||||
*/
|
*/
|
||||||
function notifyLicenseExpiration(ctx, endDate) {
|
function notifyLicenseExpiration(ctx, endDate) {
|
||||||
@ -147,27 +109,16 @@ function notifyLicenseExpiration(ctx, endDate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
const licenseEndTime = new Date(endDate);
|
if (currentDate.getTime() >= endDate.getTime() - cfgStartNotifyFrom) {
|
||||||
|
const formattedExpirationTime = humanFriendlyExpirationTime(endDate);
|
||||||
|
const tenant = tenantManager.isDefaultTenant(ctx) ? 'server' : ctx.tenant;
|
||||||
|
|
||||||
if (licenseEndTime < currentDate) {
|
const state = endDate < currentDate ? 'expired' : 'expires';
|
||||||
ctx.logger.warn(`notifyLicenseExpiration(): expiration date(${licenseEndTime}) is lesser than current date(${currentDate})`);
|
ctx.logger.warn('%s license %s on %s!!!', tenant, state, formattedExpirationTime);
|
||||||
return;
|
notificationService.notify(ctx, notificationTypes.LICENSE_EXPIRATION_WARNING, [tenant, state, formattedExpirationTime]);
|
||||||
}
|
|
||||||
|
|
||||||
if (currentDate.getTime() >= licenseEndTime.getTime() - cfgStartNotifyFrom) {
|
|
||||||
const formattedTimeRemaining = humanFriendlyExpirationTime(ctx, licenseEndTime);
|
|
||||||
let tenant = tenantManager.isDefaultTenant(ctx) ? 'server' : ctx.tenant;
|
|
||||||
ctx.logger.warn('%s license expires in %s!!!', tenant, formattedTimeRemaining);
|
|
||||||
notificationService.notify(ctx, notificationTypes.LICENSE_EXPIRED, [formattedTimeRemaining]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports.fixImageExifRotation = fixImageExifRotation;
|
||||||
fixImageExifRotation,
|
module.exports.localeToLCID = localeToLCID;
|
||||||
localeToLCID,
|
module.exports.notifyLicenseExpiration = notifyLicenseExpiration;
|
||||||
notifyLicenseExpiration
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.env.NODE_APP_INSTANCE === 'tests') {
|
|
||||||
module.exports.humanFriendlyExpirationTime = humanFriendlyExpirationTime;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -39,7 +39,6 @@ const platform = platforms[process.platform];
|
|||||||
|
|
||||||
process.env.NODE_ENV = `development-${platform}`;
|
process.env.NODE_ENV = `development-${platform}`;
|
||||||
process.env.NODE_CONFIG_DIR = '../Common/config';
|
process.env.NODE_CONFIG_DIR = '../Common/config';
|
||||||
process.env.NODE_APP_INSTANCE = 'tests';
|
|
||||||
|
|
||||||
if (platform === 'mac') {
|
if (platform === 'mac') {
|
||||||
process.env.DYLD_LIBRARY_PATH = '../FileConverter/bin/';
|
process.env.DYLD_LIBRARY_PATH = '../FileConverter/bin/';
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
const { describe, test, expect } = require('@jest/globals');
|
|
||||||
|
|
||||||
const utilsDocService = require('../../DocService/sources/utilsDocService');
|
|
||||||
const operationContext = require('../../Common/sources/operationContext');
|
|
||||||
|
|
||||||
const ctx = new operationContext.Context();
|
|
||||||
|
|
||||||
function createEndTime(day, month, year, hours, minutes) {
|
|
||||||
const date = new Date();
|
|
||||||
date.setUTCFullYear(year);
|
|
||||||
date.setUTCMonth(month);
|
|
||||||
date.setUTCDate(day);
|
|
||||||
date.setUTCHours(hours, minutes, 0,0);
|
|
||||||
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('DocService utils', function () {
|
|
||||||
describe('humanFriendlyExpirationTime() format', function () {
|
|
||||||
const currentDate = new Date();
|
|
||||||
currentDate.setUTCSeconds(0, 0);
|
|
||||||
|
|
||||||
const day = currentDate.getUTCDate();
|
|
||||||
const month = currentDate.getUTCMonth();
|
|
||||||
const year = currentDate.getUTCFullYear();
|
|
||||||
const hours = currentDate.getUTCHours();
|
|
||||||
const minutes = currentDate.getUTCMinutes();
|
|
||||||
|
|
||||||
const testSuite = {
|
|
||||||
'12 months': createEndTime(day, month, year + 1, hours, minutes),
|
|
||||||
'15 months': createEndTime(day, month + 3, year + 1, hours, minutes),
|
|
||||||
'6 months': createEndTime(day, month + 6, year, hours, minutes),
|
|
||||||
'1 month': createEndTime(day, month + 1, year, hours, minutes),
|
|
||||||
'10 days': createEndTime(day + 10, month, year, hours, minutes),
|
|
||||||
'2 days': createEndTime(day + 2, month, year, hours, minutes),
|
|
||||||
// '24 hours': createEndTime(day + 1, month, year, hours, minutes),
|
|
||||||
// '23 hours': createEndTime(day, month, year, hours + 23, minutes),
|
|
||||||
// '16 minutes': createEndTime(day, month, year, hours, minutes + 16),
|
|
||||||
// '1 hour 15 minutes': createEndTime(day, month, year, hours + 1, minutes + 15),
|
|
||||||
'': createEndTime(day, month, year - 1, hours, minutes),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const testCase in testSuite) {
|
|
||||||
test(testCase === '' ? 'wrong end date' : testCase, function () {
|
|
||||||
const result = utilsDocService.humanFriendlyExpirationTime(ctx, testSuite[testCase]);
|
|
||||||
|
|
||||||
expect(result).toEqual(testCase);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user