Skip to content

Commit

Permalink
Version 2.0:
Browse files Browse the repository at this point in the history
- Switched to ES Module
- Allow service parameters to be specified in settings
  • Loading branch information
blu3mania committed Sep 28, 2022
1 parent 90d3e48 commit 1402493
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 85 deletions.
36 changes: 27 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Disk-Space-Monitor
# disk-space-monitor
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-yellow)](https://raw.githubusercontent.com/blu3mania/disk-space-monitor/main/LICENSE)
[![node.js 10+](https://img.shields.io/badge/node.js-10.16.3-blue?logo=node.js)](https://nodejs.org/en/)
[![Latest Release](https://img.shields.io/github/v/release/blu3mania/disk-space-monitor)](https://github.com/blu3mania/disk-space-monitor/releases/latest)
Expand All @@ -7,9 +7,8 @@ Monitor disk space usage and notify user by email or system notification.

It can be run as a standalone application or as a system service.

**Note**, the instructions below uses yarn as package manager so it can install the latest minimist package
that doesn't have [Prototype Pollution vulnerability](https://www.npmjs.com/advisories/1179). Though, if you
prefer you can use npm as well, just replace "yarn" with "npm" in any command.
**Note**, this package is written as ES Module starting with 2.0. For CommonJS version, use version 1.x from
CommonJS branch.

## Run these steps first:

Expand All @@ -18,6 +17,25 @@ prefer you can use npm as well, just replace "yarn" with "npm" in any command.
instructions](https://github.com/nodejs/node-gyp#installation).

2. Edit src/settings.json.
* service defines service parameters when installed as a system service:
* name is the service name to be used.
* account info is optional. If provided, the service will be running as the specified account. These properties
can be provided:
* name is account's name, when running on Windows
* password is account's password, when running on Windows
* domain is optional, and should be provided if the account is a domain account when running on Windows
* user is the user name, when running on Linux
* group is the group name, when running on Linux
```
"service": {
"name": "Disk Space Monitor",
"account": {
"name": "{account name}",
"password": "{account password}",
"domain": "{account domain}"
}
},
```
* disks lists all the disks this script needs to montior.
* path is the path to the disk. On Windows it's usaully the drive letter followed by a colon, e.g. "C:".
On Linux it is usually the mounted file system path, e.g. "/dev".
Expand Down Expand Up @@ -54,20 +72,20 @@ prefer you can use npm as well, just replace "yarn" with "npm" in any command.
* to, cc, bcc are the email recipient. Multiple email addresses can be used with comma as delimiter.
* smtp defines the SMTP server options used by nodemailer. Please refer to [nodemail's SMTP Transport
page](https://nodemailer.com/smtp/) for details.
3. Run "yarn install". If running on Windows, accept UAC prompts if any (there could be up to 4).
3. Run "npm install". If running on Windows, accept UAC prompts if any (there could be up to 4).

**Note**, this step installs the script as a system service. If it's not desired, run "yarn run uninstall" afterwards.
**Note**, this step installs the script as a system service. If it's not desired, run "npm run uninstall" afterwards.

## To run the script manually:

Run "yarn start" or "node src/app.js".
Run "npm start" or "node src/app.js".

## To install and run the script as a system service:

Run "yarn run install" or "node src/install-service.js". If running on Windows, accept UAC prompts if any (there could be up to 4).
Run "npm run install" or "node src/install-service.js". If running on Windows, accept UAC prompts if any (there could be up to 4).

**Note**, if settings.json is updated when service is running, restart the server (on Windows, this can be done from Services control panel).

## To uninstall the system service:

Run "yarn run uninstall" or "node src/uninstall-service.js". If running on Windows, accept UAC prompts if any (there could be up to 4).
Run "npm run uninstall" or "node src/uninstall-service.js". If running on Windows, accept UAC prompts if any (there could be up to 4).
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "disk-space-monitor",
"version": "1.1.0",
"type": "module",
"version": "2.0.0",
"description": "Monitor disk space usage and notify user by email or system notification",
"main": "src/app.js",
"exports": "src/app.js",
"scripts": {
"start": "node src/app.js",
"preinstall": "npm install npm-platform-dependencies && npmpd",
Expand All @@ -11,8 +12,7 @@
},
"keywords": [
"disk space",
"disk monitor",
"disk space monitor"
"monitor"
],
"author": "blu3mania <[email protected]>",
"license": "Apache-2.0",
Expand All @@ -25,7 +25,7 @@
"url": "https://github.com/blu3mania/disk-space-monitor.git"
},
"dependencies": {
"chalk": "^4.1.2",
"chalk": "^5.0.1",
"diskusage": "^1.1.3",
"node-notifier": "^10.0.1",
"nodemailer": "^6.7.8"
Expand Down
37 changes: 22 additions & 15 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
'use strict';

const os = require('os');
const path = require('path');
const notifier = require('node-notifier');
const diskusage = require('diskusage');
const nodemailer = require('nodemailer');
const {
import os from 'os';
import path from 'path';
import url from 'url';

import diskusage from 'diskusage';
import mailer from 'nodemailer';
import notifier from 'node-notifier';

import {
error,
warning,
info,
verbose } = require('./print.js');
const settings = require('./settings.json');
verbose } from './print.js';
import settings from './settings.json' assert {type: 'json'};

const NotificationType = {
Email: 'email',
Expand All @@ -22,14 +23,20 @@ const EmailFormat = {
Text: 'text',
};

const prefixes = [
// Supported unit prefixes
const UnitPrefix = [
'k',
'm',
'g',
't',
'p',
'e',
'z',
'y',
];

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

let emailTransporter = null;

main();
Expand All @@ -38,7 +45,7 @@ function main() {
verbose('Starting...');

if (settings.notificationTypes.find(type => type.toLowerCase() === NotificationType.Email)) {
emailTransporter = nodemailer.createTransport(settings.email.smtp);
emailTransporter = mailer.createTransport(settings.email.smtp);
}

checkDiskUsage();
Expand All @@ -58,7 +65,7 @@ function checkDiskUsage() {
return Promise.reject(`Invalid configuration "${disk.threshold}" for disk "${disk.path}".`);
}
disk.notificationTriggered = false;
verbose(`Disk "${disk.path}" threshold "${disk.threshold}", which is ${disk.thresholdInBytes} bytes.`);
verbose(`Disk "${disk.path}" threshold "${disk.threshold}", which is ${disk.thresholdInBytes.toLocaleString()} bytes.`);
}
checkDiskFreeSpace(disk, diskInfo);
})
Expand Down Expand Up @@ -106,7 +113,7 @@ function calculateThresholdInBytes(disk, diskInfo) {
base = 1024;
prefixToCheck = disk.threshold.slice(-3, -2).toLowerCase();
} else {
// SI prefix
// Decimal (SI) prefix
base = 1000;
}
} else {
Expand All @@ -115,7 +122,7 @@ function calculateThresholdInBytes(disk, diskInfo) {
}

if (prefixToCheck !== null) {
const exponent = prefixes.findIndex(prefix => prefix === prefixToCheck) + 1;
const exponent = UnitPrefix.findIndex(prefix => prefix === prefixToCheck) + 1;
if (exponent === 0) {
invalidConfig = true;
} else {
Expand Down
115 changes: 79 additions & 36 deletions src/install-service.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,82 @@
'use strict';
import path from 'path';
import url from 'url';

const path = require('path');
const Service = require(process.platform === 'win32' ? 'node-windows' : process.platform === 'darwin' ? 'node-mac' : 'node-linux').Service;
const {
import {
warning,
info,
verbose } = require('./print.js');

// Create a new service object.
const svc = new Service({
name: 'Disk Space Monitor',
description: 'Monotr disk space usage and notify user by email or Windows Toast.',
script: `${path.join(__dirname, 'app.js')}`,
nodeOptions: [
'--harmony',
'--max_old_space_size=4096'
]
});

// Listen for the "install" event, which indicates the process is available as a service.
svc.on('install', () => {
verbose('Service installed.');
info('Starting service, please accept UAC prompts if any...');
svc.start();
});

svc.on('start', () => {
verbose('Service started.');
});

svc.on('alreadyinstalled', () => {
warning('Service is already installed!');
info('Starting the service in case it is not running, please accept UAC prompts if any...');
svc.start();
});

info('Installing service, please accept UAC prompts if any...');
svc.install();
verbose } from './print.js';
import settings from './settings.json' assert {type: 'json'};

main();

function main() {
// Dynamically import the module we need depending on current OS
switch (process.platform) {
case 'win32':
import('node-windows')
.then(module => installService(module.Service));
break;

case 'darwin':
import('node-mac')
.then(module => installService(module.Service));
break;

default:
import('node-linux')
.then(module => installService(module.Service));
break;
}
}

function installService(Service) {
// Create a new service object.
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
const svc = new Service({
name: settings.service?.name ?? 'Disk Space Monitor',
description: 'Monotr disk space usage and notify user by email or Windows Toast.',
script: `${path.join(__dirname, 'app.js')}`,
nodeOptions: [
'--harmony',
'--max_old_space_size=4096'
]
});

if (process.platform === 'win32') {
if (settings.service?.account?.name && settings.service?.account?.password) {
svc.logOnAs.account = settings.service.account.name;
svc.logOnAs.password = settings.service.account.password;
if (settings.service?.account?.domain) {
svc.logOnAs.domain = settings.service.account.domain;
}
}
} else if (process.platform !== 'darwin') {
if (settings.service?.account?.user) {
svc.user = settings.service.account.user;
}

if (settings.service?.account?.group) {
svc.group = settings.service.account.group;
}
}

// Listen for the "install" event, which indicates the process is available as a service.
svc.on('install', () => {
verbose('Service installed.');
info('Starting service, please accept UAC prompts if any...');
svc.start();
});

svc.on('start', () => {
verbose('Service started.');
});

svc.on('alreadyinstalled', () => {
warning('Service is already installed!');
info('Starting the service in case it is not running, please accept UAC prompts if any...');
svc.start();
});

info('Installing service, please accept UAC prompts if any...');
svc.install();
}
29 changes: 25 additions & 4 deletions src/print.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
'use strict';

const chalk = require('chalk');
import chalk from 'chalk';

const dateTimeFormatOprions = {
year: 'numeric',
Expand All @@ -12,31 +10,54 @@ const dateTimeFormatOprions = {
hour12: false
};

/** Internal method: format a message so it shows with timestamp. */
function formatMessage(msg) {
return `[${new Intl.DateTimeFormat('en-US', dateTimeFormatOprions).format(new Date())}] ${typeof msg === 'string' ? msg : JSON.stringify(msg, null, 2)}`;
}

/**
* Prints a message in console.
* @param {string|Object} msg - The message to be printed. It can be a non-string type, in which case it will be serialized before printing.
* @param {Chalk} color - (Optional) The color of the message to be printed.
* If not provided, default color white is used.
*/
function print(msg, color = chalk.white) {
console.log(color(formatMessage(msg)));
}

/**
* Prints an error message in console.
* @param {string|Object} msg - The error message to be printed. It can be a non-string type, in which case it will be serialized before printing.
*/
function error(msg) {
console.log(chalk.red(formatMessage(msg)));
}

/**
* Prints a warning message in console.
* @param {string|Object} msg - The warning message to be printed. It can be a non-string type, in which case it will be serialized before printing.
*/
function warning(msg) {
console.log(chalk.yellow(formatMessage(msg)));
}

/**
* Prints an infomation message in console.
* @param {string|Object} msg - The information message to be printed. It can be a non-string type, in which case it will be serialized before printing.
*/
function info(msg) {
console.log(chalk.cyan(formatMessage(msg)));
}

/**
* Prints a verbose message in console.
* @param {string|Object} msg - The verbose message to be printed. It can be a non-string type, in which case it will be serialized before printing.
*/
function verbose(msg) {
console.log(chalk.green(formatMessage(msg)));
}

module.exports = {
export {
print,
error,
warning,
Expand Down
3 changes: 3 additions & 0 deletions src/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"service": {
"name": "Disk Space Monitor"
},
"disks": [
{
"path": "C:",
Expand Down
Loading

0 comments on commit 1402493

Please sign in to comment.