fix(update): proper require path fixes + license-keygen restore for v1.14.4 compatibility
v1.14.4 (commit d2a48b1) shipped with broken relative paths and missing
license-keygen.js module. This commit:
- server.js: 26 '../src/...' requires rewritten to './src/...' (server is
at API root, must use ./src for files in src/)
- src/managers/license-manager.js: './license-keygen' rewritten to
'../../license-keygen' (license-keygen.js lives at API root)
- src/docker/self-updater.js: './platform-paths' rewritten to
'../../platform-paths' (platform-paths.js lives at API root)
- license-keygen.js: restored to root (was missing from v1.14.4 tarball)
- VERSION: bumped to d2a48b1-patched (matches upstream commit but with
our fixes baked in)
Makes the v1.14.4 source buildable and runnable without external patches.
Companion to scripts/dashcaddy-post-deploy-patches.sh which applies these
fixes automatically during the host-side update flow.
This commit is contained in:
@@ -1 +1 @@
|
|||||||
1.13.4
|
d2a48b1
|
||||||
|
|||||||
@@ -0,0 +1,313 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* DashCaddy License Code Generator
|
||||||
|
*
|
||||||
|
* Admin-only CLI tool for generating license codes.
|
||||||
|
* NOT shipped with the product — runs only on the developer's machine.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node license-keygen.js --duration 365 --count 10
|
||||||
|
* node license-keygen.js --duration 30 --count 1 --output codes.txt
|
||||||
|
* node license-keygen.js --verify DC-XXXXX-XXXXX-XXXXX-XXXXX
|
||||||
|
* node license-keygen.js --init-secret
|
||||||
|
*/
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Master secret file — lives only on admin machine, NEVER shipped
|
||||||
|
const SECRET_FILE = path.join(__dirname, '.license-secret');
|
||||||
|
|
||||||
|
// License code format: DC-AAAAA-BBBBB-CCCCC-DDDDD
|
||||||
|
// Encodes: version(4bit) + duration_days(12bit) + code_id(32bit) + created_ts(32bit) + hmac(48bit)
|
||||||
|
// Total: 128 bits = 16 bytes, base32-encoded into 4 groups of 5 chars
|
||||||
|
|
||||||
|
const VALID_DURATIONS = [30, 90, 180, 365];
|
||||||
|
const LIFETIME_DURATION = 0; // Admin-only, not publicly available
|
||||||
|
const VERSION = 1;
|
||||||
|
|
||||||
|
// Base32 alphabet (Crockford variant — no I/L/O/U to avoid confusion)
|
||||||
|
const BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||||
|
|
||||||
|
function base32Encode(buffer) {
|
||||||
|
let bits = '';
|
||||||
|
for (const byte of buffer) {
|
||||||
|
bits += byte.toString(2).padStart(8, '0');
|
||||||
|
}
|
||||||
|
// Pad to multiple of 5
|
||||||
|
while (bits.length % 5 !== 0) bits += '0';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < bits.length; i += 5) {
|
||||||
|
const index = parseInt(bits.substring(i, i + 5), 2);
|
||||||
|
result += BASE32[index];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function base32Decode(str) {
|
||||||
|
let bits = '';
|
||||||
|
for (const char of str.toUpperCase()) {
|
||||||
|
const index = BASE32.indexOf(char);
|
||||||
|
if (index === -1) throw new Error(`Invalid base32 character: ${char}`);
|
||||||
|
bits += index.toString(2).padStart(5, '0');
|
||||||
|
}
|
||||||
|
const bytes = [];
|
||||||
|
for (let i = 0; i + 8 <= bits.length; i += 8) {
|
||||||
|
bytes.push(parseInt(bits.substring(i, i + 8), 2));
|
||||||
|
}
|
||||||
|
return Buffer.from(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSecret() {
|
||||||
|
if (!fs.existsSync(SECRET_FILE)) {
|
||||||
|
console.error('No master secret found. Run with --init-secret first.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return fs.readFileSync(SECRET_FILE, 'utf8').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSecret() {
|
||||||
|
if (fs.existsSync(SECRET_FILE)) {
|
||||||
|
console.error('Master secret already exists at', SECRET_FILE);
|
||||||
|
console.error('Delete it first if you want to regenerate (WARNING: invalidates all existing codes).');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const secret = crypto.randomBytes(32).toString('hex');
|
||||||
|
fs.writeFileSync(SECRET_FILE, secret, { mode: 0o600 });
|
||||||
|
console.log('Master secret generated and saved to', SECRET_FILE);
|
||||||
|
console.log('KEEP THIS FILE SAFE. It is needed to generate and validate all license codes.');
|
||||||
|
console.log('DO NOT ship this file with the product.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCode(secret, durationDays, codeId) {
|
||||||
|
// Pack payload: version(4b) + duration_days(12b) + code_id(32b) + created_ts(32b) = 80 bits = 10 bytes
|
||||||
|
const payload = Buffer.alloc(10);
|
||||||
|
|
||||||
|
// Byte 0-1: version (4 bits) + duration (12 bits) = 16 bits
|
||||||
|
const versionAndDuration = ((VERSION & 0x0F) << 12) | (durationDays & 0x0FFF);
|
||||||
|
payload.writeUInt16BE(versionAndDuration, 0);
|
||||||
|
|
||||||
|
// Byte 2-5: code_id (32 bits)
|
||||||
|
payload.writeUInt32BE(codeId, 2);
|
||||||
|
|
||||||
|
// Byte 6-9: created timestamp (32 bits, seconds since epoch)
|
||||||
|
const createdTs = Math.floor(Date.now() / 1000);
|
||||||
|
payload.writeUInt32BE(createdTs, 6);
|
||||||
|
|
||||||
|
// HMAC the payload to get signature
|
||||||
|
const hmac = crypto.createHmac('sha256', secret).update(payload).digest();
|
||||||
|
// Take first 5 bytes of HMAC (40 bits) — fits exactly in 25 base32 chars with 10-byte payload
|
||||||
|
const signature = hmac.subarray(0, 5);
|
||||||
|
|
||||||
|
// Combine: payload (10 bytes) + signature (5 bytes) = 15 bytes = 120 bits
|
||||||
|
// 25 base32 chars = 125 bits, comfortably fits 120 bits
|
||||||
|
const combined = Buffer.concat([payload, signature]);
|
||||||
|
|
||||||
|
let encoded = base32Encode(combined);
|
||||||
|
while (encoded.length < 25) encoded += '0';
|
||||||
|
encoded = encoded.substring(0, 25);
|
||||||
|
const groups = [];
|
||||||
|
for (let i = 0; i < 25; i += 5) {
|
||||||
|
groups.push(encoded.substring(i, i + 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
return `DC-${groups.join('-')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCode(code) {
|
||||||
|
// Strip prefix and dashes
|
||||||
|
const cleaned = code.replace(/^DC-/, '').replace(/-/g, '');
|
||||||
|
if (cleaned.length !== 25) {
|
||||||
|
throw new Error(`Invalid code length: expected 25 base32 chars, got ${cleaned.length}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode base32 — 25 chars = 125 bits = 15 full bytes
|
||||||
|
const decoded = base32Decode(cleaned);
|
||||||
|
if (decoded.length < 15) {
|
||||||
|
const padded = Buffer.alloc(15);
|
||||||
|
decoded.copy(padded);
|
||||||
|
return parsePayload(padded);
|
||||||
|
}
|
||||||
|
return parsePayload(decoded.subarray(0, 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePayload(buffer) {
|
||||||
|
const payload = buffer.subarray(0, 10);
|
||||||
|
const signature = buffer.subarray(10, 15);
|
||||||
|
|
||||||
|
const versionAndDuration = payload.readUInt16BE(0);
|
||||||
|
const version = (versionAndDuration >> 12) & 0x0F;
|
||||||
|
const durationDays = versionAndDuration & 0x0FFF;
|
||||||
|
const codeId = payload.readUInt32BE(2);
|
||||||
|
const createdTs = payload.readUInt32BE(6);
|
||||||
|
|
||||||
|
return { version, durationDays, codeId, createdTs, payload, signature };
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyCode(secret, code) {
|
||||||
|
try {
|
||||||
|
const { version, durationDays, codeId, createdTs, payload, signature } = parseCode(code);
|
||||||
|
|
||||||
|
// Verify HMAC (5-byte signature)
|
||||||
|
const expectedHmac = crypto.createHmac('sha256', secret).update(payload).digest();
|
||||||
|
const expectedSig = expectedHmac.subarray(0, 5);
|
||||||
|
|
||||||
|
if (!crypto.timingSafeEqual(signature, expectedSig)) {
|
||||||
|
return { valid: false, reason: 'Invalid signature — code is forged or corrupted' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version !== VERSION) {
|
||||||
|
return { valid: false, reason: `Unsupported version: ${version}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept lifetime (0) and standard durations
|
||||||
|
if (durationDays !== LIFETIME_DURATION && !VALID_DURATIONS.includes(durationDays)) {
|
||||||
|
return { valid: false, reason: `Invalid duration: ${durationDays} days` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdDate = new Date(createdTs * 1000);
|
||||||
|
const isLifetime = durationDays === LIFETIME_DURATION;
|
||||||
|
const expiresDate = isLifetime ? null : new Date(createdTs * 1000 + durationDays * 86400000);
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
version,
|
||||||
|
durationDays,
|
||||||
|
codeId,
|
||||||
|
createdAt: createdDate.toISOString(),
|
||||||
|
expiresAt: isLifetime ? null : expiresDate.toISOString(),
|
||||||
|
expired: isLifetime ? false : Date.now() > expiresDate.getTime()
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { valid: false, reason: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI
|
||||||
|
function main() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (args.includes('--help') || args.length === 0) {
|
||||||
|
console.log(`
|
||||||
|
DashCaddy License Code Generator
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
node license-keygen.js --init-secret Initialize master secret (first time only)
|
||||||
|
node license-keygen.js --duration <days> [options] Generate license codes
|
||||||
|
node license-keygen.js --verify <code> Verify a license code
|
||||||
|
node license-keygen.js --decode <code> Decode and display code details
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--duration <days> Code validity: 30, 90, 180, or 365 days (required for generation)
|
||||||
|
--count <n> Number of codes to generate (default: 1)
|
||||||
|
--start-id <n> Starting code ID (default: auto from counter file)
|
||||||
|
--output <file> Write codes to file instead of stdout
|
||||||
|
--json Output as JSON
|
||||||
|
|
||||||
|
Valid durations: ${VALID_DURATIONS.join(', ')} days
|
||||||
|
`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('--init-secret')) {
|
||||||
|
initSecret();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.includes('--verify') || args.includes('--decode')) {
|
||||||
|
const codeIndex = args.indexOf('--verify') !== -1 ? args.indexOf('--verify') : args.indexOf('--decode');
|
||||||
|
const code = args[codeIndex + 1];
|
||||||
|
if (!code) {
|
||||||
|
console.error('Please provide a code to verify.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const secret = getSecret();
|
||||||
|
const result = verifyCode(secret, code);
|
||||||
|
if (args.includes('--json')) {
|
||||||
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
} else if (result.valid) {
|
||||||
|
const isLifetime = result.durationDays === 0;
|
||||||
|
console.log('Code is VALID');
|
||||||
|
console.log(` Version: ${result.version}`);
|
||||||
|
console.log(` Duration: ${isLifetime ? 'LIFETIME' : result.durationDays + ' days'}`);
|
||||||
|
console.log(` Code ID: ${result.codeId}`);
|
||||||
|
console.log(` Created: ${result.createdAt}`);
|
||||||
|
console.log(` Expires: ${isLifetime ? 'NEVER' : result.expiresAt}`);
|
||||||
|
console.log(` Status: ${isLifetime ? 'LIFETIME' : (result.expired ? 'EXPIRED' : 'ACTIVE')}`);
|
||||||
|
} else {
|
||||||
|
console.log('Code is INVALID');
|
||||||
|
console.log(` Reason: ${result.reason}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate codes
|
||||||
|
const isLifetime = args.includes('--lifetime');
|
||||||
|
const durationIndex = args.indexOf('--duration');
|
||||||
|
if (!isLifetime && durationIndex === -1) {
|
||||||
|
console.error('--duration is required. Use --help for usage.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const duration = isLifetime ? LIFETIME_DURATION : parseInt(args[durationIndex + 1]);
|
||||||
|
if (!isLifetime && !VALID_DURATIONS.includes(duration)) {
|
||||||
|
console.error(`Invalid duration: ${duration}. Valid: ${VALID_DURATIONS.join(', ')}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const countIndex = args.indexOf('--count');
|
||||||
|
const count = countIndex !== -1 ? parseInt(args[countIndex + 1]) : 1;
|
||||||
|
|
||||||
|
// Load or create counter file for auto-incrementing code IDs
|
||||||
|
const counterFile = path.join(__dirname, '.license-counter');
|
||||||
|
let startId;
|
||||||
|
const startIdIndex = args.indexOf('--start-id');
|
||||||
|
if (startIdIndex !== -1) {
|
||||||
|
startId = parseInt(args[startIdIndex + 1]);
|
||||||
|
} else if (fs.existsSync(counterFile)) {
|
||||||
|
startId = parseInt(fs.readFileSync(counterFile, 'utf8').trim()) + 1;
|
||||||
|
} else {
|
||||||
|
startId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secret = getSecret();
|
||||||
|
const codes = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const codeId = startId + i;
|
||||||
|
const code = generateCode(secret, duration, codeId);
|
||||||
|
codes.push({ code, codeId, durationDays: duration });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save counter
|
||||||
|
fs.writeFileSync(counterFile, String(startId + count - 1));
|
||||||
|
|
||||||
|
// Output
|
||||||
|
const outputIndex = args.indexOf('--output');
|
||||||
|
if (args.includes('--json')) {
|
||||||
|
const output = JSON.stringify(codes, null, 2);
|
||||||
|
if (outputIndex !== -1) {
|
||||||
|
fs.writeFileSync(args[outputIndex + 1], output);
|
||||||
|
console.log(`${count} code(s) written to ${args[outputIndex + 1]}`);
|
||||||
|
} else {
|
||||||
|
console.log(output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const lines = codes.map(c => `${c.code} (${c.durationDays === 0 ? 'LIFETIME' : c.durationDays + ' days'}, ID: ${c.codeId})`);
|
||||||
|
if (outputIndex !== -1) {
|
||||||
|
fs.writeFileSync(args[outputIndex + 1], codes.map(c => c.code).join('\n') + '\n');
|
||||||
|
console.log(`${count} code(s) written to ${args[outputIndex + 1]}`);
|
||||||
|
} else {
|
||||||
|
lines.forEach(l => console.log(l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nGenerated ${count} code(s) for ${duration === 0 ? 'LIFETIME' : duration + ' days'}. Next ID: ${startId + count}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also export for use by license-manager.js
|
||||||
|
module.exports = { verifyCode, parseCode, VALID_DURATIONS, VERSION };
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
+21
-21
@@ -34,7 +34,7 @@ process.on('uncaughtException', (error) => {
|
|||||||
const CONFIG_FILE = process.env.CONFIG_FILE || platformPaths.servicesFile.replace('services.json', 'config.json');
|
const CONFIG_FILE = process.env.CONFIG_FILE || platformPaths.servicesFile.replace('services.json', 'config.json');
|
||||||
|
|
||||||
// Validate startup configuration
|
// Validate startup configuration
|
||||||
const { validateStartupConfig } = require('../src/utilities/startup-validator');
|
const { validateStartupConfig } = require('./src/utilities/startup-validator');
|
||||||
await validateStartupConfig({
|
await validateStartupConfig({
|
||||||
log,
|
log,
|
||||||
CADDYFILE_PATH,
|
CADDYFILE_PATH,
|
||||||
@@ -57,17 +57,17 @@ process.on('uncaughtException', (error) => {
|
|||||||
|
|
||||||
// Attach WebSocket exec handler (with auth)
|
// Attach WebSocket exec handler (with auth)
|
||||||
const attachExecWS = require('./routes/exec');
|
const attachExecWS = require('./routes/exec');
|
||||||
const authManager = require('../src/managers/auth-manager');
|
const authManager = require('./src/managers/auth-manager');
|
||||||
attachExecWS(server, log, authManager);
|
attachExecWS(server, log, authManager);
|
||||||
log.info('server', 'WebSocket exec handler attached (auth enforced)');
|
log.info('server', 'WebSocket exec handler attached (auth enforced)');
|
||||||
|
|
||||||
// Start feature modules
|
// Start feature modules
|
||||||
const resourceMonitor = require('../src/managers/resource-monitor');
|
const resourceMonitor = require('./src/managers/resource-monitor');
|
||||||
const backupManager = require('../src/utilities/backup-manager');
|
const backupManager = require('./src/utilities/backup-manager');
|
||||||
const healthChecker = require('../src/monitoring/health-checker');
|
const healthChecker = require('./src/monitoring/health-checker');
|
||||||
const updateManager = require('../src/managers/update-manager');
|
const updateManager = require('./src/managers/update-manager');
|
||||||
const selfUpdater = require('../src/docker/self-updater');
|
const selfUpdater = require('./src/docker/self-updater');
|
||||||
const portLockManager = require('../src/managers/port-lock-manager');
|
const portLockManager = require('./src/managers/port-lock-manager');
|
||||||
|
|
||||||
// Create servicesStateManager early — needed by workflow engine init
|
// Create servicesStateManager early — needed by workflow engine init
|
||||||
const StateManager = require('./state-manager');
|
const StateManager = require('./state-manager');
|
||||||
@@ -75,9 +75,9 @@ process.on('uncaughtException', (error) => {
|
|||||||
|
|
||||||
// Optional modules
|
// Optional modules
|
||||||
let dockerMaintenance, logDigest, bundledWorkflows;
|
let dockerMaintenance, logDigest, bundledWorkflows;
|
||||||
try { dockerMaintenance = require('../src/docker/docker-maintenance'); } catch { /* optional */ }
|
try { dockerMaintenance = require('./src/docker/docker-maintenance'); } catch { /* optional */ }
|
||||||
try { logDigest = require('../src/security/log-digest'); } catch { /* optional */ }
|
try { logDigest = require('./src/security/log-digest'); } catch { /* optional */ }
|
||||||
try { bundledWorkflows = require('../src/recipes/bundled-workflows'); } catch { /* optional */ }
|
try { bundledWorkflows = require('./src/recipes/bundled-workflows'); } catch { /* optional */ }
|
||||||
|
|
||||||
// Initialize workflow engine if bundled-workflows is available
|
// Initialize workflow engine if bundled-workflows is available
|
||||||
// NOTE: createApp() already initializes the workflow engine in src/app.js
|
// NOTE: createApp() already initializes the workflow engine in src/app.js
|
||||||
@@ -142,8 +142,8 @@ process.on('uncaughtException', (error) => {
|
|||||||
// Health checker (with service sync)
|
// Health checker (with service sync)
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const { syncHealthCheckerServices } = require('../src/utilities/startup-validator');
|
const { syncHealthCheckerServices } = require('./src/utilities/startup-validator');
|
||||||
const StateManager = require('../src/managers/state-manager');
|
const StateManager = require('./src/managers/state-manager');
|
||||||
const servicesStateManager = new StateManager(SERVICES_FILE);
|
const servicesStateManager = new StateManager(SERVICES_FILE);
|
||||||
|
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ process.on('uncaughtException', (error) => {
|
|||||||
? `https://${config.domain}/${subdomain}`
|
? `https://${config.domain}/${subdomain}`
|
||||||
: `https://${subdomain}${config.tld}`,
|
: `https://${subdomain}${config.tld}`,
|
||||||
siteConfig: config,
|
siteConfig: config,
|
||||||
APP: require('../src/utilities/constants').APP
|
APP: require('./src/utilities/constants').APP
|
||||||
});
|
});
|
||||||
|
|
||||||
healthChecker.start();
|
healthChecker.start();
|
||||||
@@ -238,11 +238,11 @@ process.on('uncaughtException', (error) => {
|
|||||||
const shutdown = (signal) => {
|
const shutdown = (signal) => {
|
||||||
log.info('shutdown', `${signal} received, draining connections...`);
|
log.info('shutdown', `${signal} received, draining connections...`);
|
||||||
|
|
||||||
const resourceMonitor = require('../src/managers/resource-monitor');
|
const resourceMonitor = require('./src/managers/resource-monitor');
|
||||||
const backupManager = require('../src/utilities/backup-manager');
|
const backupManager = require('./src/utilities/backup-manager');
|
||||||
const healthChecker = require('../src/monitoring/health-checker');
|
const healthChecker = require('./src/monitoring/health-checker');
|
||||||
const updateManager = require('../src/managers/update-manager');
|
const updateManager = require('./src/managers/update-manager');
|
||||||
const selfUpdater = require('../src/docker/self-updater');
|
const selfUpdater = require('./src/docker/self-updater');
|
||||||
|
|
||||||
resourceMonitor.stop();
|
resourceMonitor.stop();
|
||||||
backupManager.stop();
|
backupManager.stop();
|
||||||
@@ -251,12 +251,12 @@ process.on('uncaughtException', (error) => {
|
|||||||
selfUpdater.stop();
|
selfUpdater.stop();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dockerMaintenance = require('../src/docker/docker-maintenance');
|
const dockerMaintenance = require('./src/docker/docker-maintenance');
|
||||||
dockerMaintenance.stop();
|
dockerMaintenance.stop();
|
||||||
} catch { /* optional */ }
|
} catch { /* optional */ }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const logDigest = require('../src/security/log-digest');
|
const logDigest = require('./src/security/log-digest');
|
||||||
logDigest.stop();
|
logDigest.stop();
|
||||||
} catch { /* optional */ }
|
} catch { /* optional */ }
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const crypto = require('crypto');
|
|||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { verifyCode, parseCode, VALID_DURATIONS } = require('./license-keygen');
|
const { verifyCode, parseCode, VALID_DURATIONS } = require('../../license-keygen');
|
||||||
const { errorResponse } = require('../utils/responses');
|
const { errorResponse } = require('../utils/responses');
|
||||||
|
|
||||||
const LICENSE_CRED_KEY = 'license.activation';
|
const LICENSE_CRED_KEY = 'license.activation';
|
||||||
|
|||||||
Reference in New Issue
Block a user