mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-21 09:52:06 +03:00
c7448c46bd
Refs #6643
277 lines
9.7 KiB
JavaScript
277 lines
9.7 KiB
JavaScript
var packages = require('../../../package.json'),
|
|
path = require('path'),
|
|
crypto = require('crypto'),
|
|
fs = require('fs'),
|
|
mode = process.env.NODE_ENV === undefined ? 'development' : process.env.NODE_ENV,
|
|
appRoot = path.resolve(__dirname, '../../../'),
|
|
configFilePath = process.env.GHOST_CONFIG || path.join(appRoot, 'config.js'),
|
|
checks,
|
|
exitCodes = {
|
|
NODE_VERSION_UNSUPPORTED: 231,
|
|
NODE_ENV_CONFIG_MISSING: 232,
|
|
DEPENDENCIES_MISSING: 233,
|
|
CONTENT_PATH_NOT_ACCESSIBLE: 234,
|
|
CONTENT_PATH_NOT_WRITABLE: 235,
|
|
SQLITE_DB_NOT_WRITABLE: 236,
|
|
BUILT_FILES_DO_NOT_EXIST: 237
|
|
};
|
|
|
|
checks = {
|
|
check: function check() {
|
|
this.nodeVersion();
|
|
this.nodeEnv();
|
|
this.packages();
|
|
this.contentPath();
|
|
this.mail();
|
|
this.sqlite();
|
|
this.builtFilesExist();
|
|
},
|
|
|
|
// Make sure the node version is supported
|
|
nodeVersion: function checkNodeVersion() {
|
|
// Tell users if their node version is not supported, and exit
|
|
var semver = require('semver');
|
|
|
|
if (process.env.GHOST_NODE_VERSION_CHECK !== 'false' &&
|
|
!semver.satisfies(process.versions.node, packages.engines.node)) {
|
|
console.error('\x1B[31mERROR: Unsupported version of Node');
|
|
console.error('\x1B[31mGhost needs Node version ' + packages.engines.node +
|
|
' you are using version ' + process.versions.node + '\033[0m\n');
|
|
console.error('\x1B[32mPlease see http://support.ghost.org/supported-node-versions/ for more information\033[0m');
|
|
|
|
process.exit(exitCodes.NODE_VERSION_UNSUPPORTED);
|
|
}
|
|
},
|
|
|
|
nodeEnv: function checkNodeEnvState() {
|
|
// Check if config path resolves, if not check for NODE_ENV in config.example.js prior to copy
|
|
var fd,
|
|
configFile,
|
|
config;
|
|
|
|
try {
|
|
fd = fs.openSync(configFilePath, 'r');
|
|
fs.closeSync(fd);
|
|
} catch (e) {
|
|
configFilePath = path.join(appRoot, 'config.example.js');
|
|
}
|
|
|
|
configFile = require(configFilePath);
|
|
config = configFile[mode];
|
|
|
|
if (!config) {
|
|
console.error('\x1B[31mERROR: Cannot find the configuration for the current NODE_ENV: ' +
|
|
process.env.NODE_ENV + '\033[0m\n');
|
|
console.error('\x1B[32mEnsure your config.js has a section for the current NODE_ENV value' +
|
|
' and is formatted properly.\033[0m');
|
|
|
|
process.exit(exitCodes.NODE_ENV_CONFIG_MISSING);
|
|
}
|
|
},
|
|
|
|
// Make sure package.json dependencies have been installed.
|
|
packages: function checkPackages() {
|
|
if (mode !== 'production' && mode !== 'development') {
|
|
return;
|
|
}
|
|
|
|
var errors = [];
|
|
|
|
Object.keys(packages.dependencies).forEach(function (p) {
|
|
try {
|
|
require.resolve(p);
|
|
} catch (e) {
|
|
errors.push(e.message);
|
|
}
|
|
});
|
|
|
|
if (!errors.length) {
|
|
return;
|
|
}
|
|
|
|
errors = errors.join('\n ');
|
|
|
|
console.error('\x1B[31mERROR: Ghost is unable to start due to missing dependencies:\033[0m\n ' + errors);
|
|
console.error('\x1B[32m\nPlease run `npm install --production` and try starting Ghost again.');
|
|
console.error('\x1B[32mHelp and documentation can be found at http://support.ghost.org.\033[0m\n');
|
|
|
|
process.exit(exitCodes.DEPENDENCIES_MISSING);
|
|
},
|
|
|
|
// Check content path permissions
|
|
contentPath: function checkContentPaths() {
|
|
if (mode !== 'production' && mode !== 'development') {
|
|
return;
|
|
}
|
|
|
|
var configFile,
|
|
config,
|
|
contentPath,
|
|
contentSubPaths = ['apps', 'data', 'images', 'themes'],
|
|
fd,
|
|
errorHeader = '\x1B[31mERROR: Unable to access Ghost\'s content path:\033[0m',
|
|
errorHelp = '\x1B[32mCheck that the content path exists and file system permissions are correct.' +
|
|
'\nHelp and documentation can be found at http://support.ghost.org.\033[0m';
|
|
|
|
// Get the content path to test. If it's defined in config.js use that, if not use the default
|
|
try {
|
|
configFile = require(configFilePath);
|
|
config = configFile[mode];
|
|
|
|
if (config && config.paths && config.paths.contentPath) {
|
|
contentPath = config.paths.contentPath;
|
|
} else {
|
|
contentPath = path.join(appRoot, 'content');
|
|
}
|
|
} catch (e) {
|
|
// If config.js doesn't exist yet, check the default content path location
|
|
contentPath = path.join(appRoot, 'content');
|
|
}
|
|
|
|
// Use all sync io calls so that we stay in this function until all checks are complete
|
|
|
|
// Check the root content path
|
|
try {
|
|
fd = fs.openSync(contentPath, 'r');
|
|
fs.closeSync(fd);
|
|
} catch (e) {
|
|
console.error(errorHeader);
|
|
console.error(' ' + e.message);
|
|
console.error('\n' + errorHelp);
|
|
|
|
process.exit(exitCodes.CONTENT_PATH_NOT_ACCESSIBLE);
|
|
}
|
|
|
|
// Check each of the content path subdirectories
|
|
try {
|
|
contentSubPaths.forEach(function (sub) {
|
|
var dir = path.join(contentPath, sub),
|
|
randomFile = path.join(dir, crypto.randomBytes(8).toString('hex'));
|
|
|
|
fd = fs.openSync(dir, 'r');
|
|
fs.closeSync(fd);
|
|
|
|
// Check write access to directory by attempting to create a random file
|
|
fd = fs.openSync(randomFile, 'wx+');
|
|
fs.closeSync(fd);
|
|
fs.unlinkSync(randomFile);
|
|
});
|
|
} catch (e) {
|
|
console.error(errorHeader);
|
|
console.error(' ' + e.message);
|
|
console.error('\n' + errorHelp);
|
|
|
|
process.exit(exitCodes.CONTENT_PATH_NOT_WRITABLE);
|
|
}
|
|
},
|
|
|
|
// Make sure sqlite3 database is available for read/write
|
|
sqlite: function checkSqlite() {
|
|
if (mode !== 'production' && mode !== 'development') {
|
|
return;
|
|
}
|
|
|
|
var configFile,
|
|
config,
|
|
appRoot = path.resolve(__dirname, '../../../'),
|
|
dbPath,
|
|
fd;
|
|
|
|
try {
|
|
configFile = require(configFilePath);
|
|
config = configFile[mode];
|
|
|
|
// Abort check if database type is not sqlite3
|
|
if (config && config.database && config.database.client !== 'sqlite3') {
|
|
return;
|
|
}
|
|
|
|
if (config && config.database && config.database.connection) {
|
|
dbPath = config.database.connection.filename;
|
|
}
|
|
} catch (e) {
|
|
// If config.js doesn't exist, use the default path
|
|
dbPath = path.join(appRoot, 'content', 'data', mode === 'production' ? 'ghost.db' : 'ghost-dev.db');
|
|
}
|
|
|
|
// Check for read/write access on sqlite db file
|
|
try {
|
|
fd = fs.openSync(dbPath, 'r+');
|
|
fs.closeSync(fd);
|
|
} catch (e) {
|
|
// Database file not existing is not an error as sqlite will create it.
|
|
if (e.code === 'ENOENT') {
|
|
return;
|
|
}
|
|
|
|
console.error('\x1B[31mERROR: Unable to open sqlite3 database file for read/write\033[0m');
|
|
console.error(' ' + e.message);
|
|
console.error('\n\x1B[32mCheck that the sqlite3 database file permissions allow read and write access.');
|
|
console.error('Help and documentation can be found at http://support.ghost.org.\033[0m');
|
|
|
|
process.exit(exitCodes.SQLITE_DB_NOT_WRITABLE);
|
|
}
|
|
},
|
|
|
|
mail: function checkMail() {
|
|
var configFile,
|
|
config;
|
|
|
|
try {
|
|
configFile = require(configFilePath);
|
|
config = configFile[mode];
|
|
} catch (e) {
|
|
configFilePath = path.join(appRoot, 'config.example.js');
|
|
}
|
|
|
|
if (!config.mail || !config.mail.transport) {
|
|
console.error('\x1B[31mWARNING: Ghost is attempting to use a direct method to send email. \nIt is recommended that you explicitly configure an email service.\033[0m');
|
|
console.error('\x1B[32mHelp and documentation can be found at http://support.ghost.org/mail.\033[0m\n');
|
|
}
|
|
},
|
|
|
|
builtFilesExist: function builtFilesExist() {
|
|
var configFile,
|
|
config,
|
|
location,
|
|
fileNames = ['ghost.js', 'vendor.js', 'ghost.css', 'vendor.css'];
|
|
|
|
try {
|
|
configFile = require(configFilePath);
|
|
config = configFile[mode];
|
|
|
|
if (config.paths && config.paths.clientAssets) {
|
|
location = config.paths.clientAssets;
|
|
} else {
|
|
location = path.join(appRoot, '/core/built/assets/');
|
|
}
|
|
} catch (e) {
|
|
location = path.join(appRoot, '/core/built/assets/');
|
|
}
|
|
|
|
if (process.env.NODE_ENV === 'production') {
|
|
// Production uses `.min` files
|
|
fileNames = fileNames.map(function (file) {
|
|
return file.replace('.', '.min.');
|
|
});
|
|
}
|
|
|
|
function checkExist(fileName) {
|
|
try {
|
|
fs.statSync(fileName);
|
|
} catch (e) {
|
|
console.error('\x1B[31mERROR: Javascript files have not been built.\033[0m');
|
|
console.error('\n\x1B[32mPlease read the getting started instructions at:');
|
|
console.error('https://github.com/TryGhost/Ghost#getting-started\033[0m');
|
|
process.exit(exitCodes.BUILT_FILES_DO_NOT_EXIST);
|
|
}
|
|
}
|
|
|
|
fileNames.forEach(function (fileName) {
|
|
checkExist(location + fileName);
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = checks;
|