The DTP Sites web app development engine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

279 lines
7.5 KiB

// dtp-sites-cli.js
// Copryright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0
'use strict';
const DTP_COMPONENT = { name: 'Sites CLI', slug: 'sites-cli' };
require('dotenv').config();
const path = require('path');
const mongoose = require('mongoose');
const generatePassword = require('password-generator');
const {
SitePlatform,
SiteAsync,
SiteLog,
} = require(path.join(__dirname, 'lib', 'site-lib'));
module.rootPath = __dirname;
module.pkg = require(path.join(module.rootPath, 'package.json'));
module.config = {
component: DTP_COMPONENT,
root: module.rootPath,
site: require(path.join(module.rootPath, 'config', 'site')),
http: require(path.join(module.rootPath, 'config', 'http')),
};
module.log = new SiteLog(module, module.config.component);
module.grantPermission = async (target, permission) => {
const User = mongoose.model('User');
try {
const user = await User.findOne({ email: target }).select('+permissions +flags');
switch (permission) {
case 'admin':
user.flags.isAdmin = true;
break;
case 'moderator':
user.flags.isModerator = true;
break;
case 'login':
user.permissions.canLogin = true;
break;
default:
throw new Error(`Invalid permission: ${permission}`);
}
await user.save();
module.log.info('user updated', { user: user._id });
} catch (error) {
module.log.error('failed to grant permission', { app: module.app, target, permission, error });
}
};
module.revokePermission = async (target, permission) => {
const User = mongoose.model('User');
try {
const user = await User.findOne({ email: target }).select('+permissions +flags');
switch (permission) {
case 'admin':
user.flags.isAdmin = false;
break;
case 'moderator':
user.flags.isModerator = false;
break;
case 'login':
user.permissions.canLogin = false;
break;
default:
throw new Error(`Invalid permission: ${permission}`);
}
await user.save();
module.log.info('user updated', { user: user._id });
} catch (error) {
module.log.error('failed to revoke permission', { app: module.app, target, permission, error });
}
};
module.deleteOtpAccount = async (target) => {
const { otpAuth: otpAuthService } = module.services;
const User = mongoose.model('User');
try {
const user = await User.findOne({ email: target }).lean();
const response = await otpAuthService.removeForUser(user);
module.log.info('OTP accounts removed', { userId: user._id, response });
} catch (error) {
module.log.error('failed to remove OTP account', { target, error });
}
};
module.makeBucket = async (target) => {
const { minio: minioService } = module.services;
try {
module.log.info('creating bucket', { target });
await minioService.makeBucket(target, 'us-east-1');
} catch (error) {
module.log.error('failed to create bucket', { target, error });
}
};
module.resetIndexes = async ( ) => {
try {
module.log.info('resetting MongoDB indexes...');
await SitePlatform.resetIndexes(module);
} catch (error) {
module.log.error('failed to reset database indexes', { error });
}
};
module.resetPassword = async (username) => {
const { user: userService } = module.services;
const User = mongoose.model('User');
const user = await User
.findOne({
username_lc: username.trim().toLowerCase(),
})
.lean();
if (!user) {
throw new Error('user not found');
}
const password = generatePassword(8, true);
module.log.info('updating user password', { username, password });
await userService.updatePassword(user, password);
};
module.sendWelcomeEmail = async (username) => {
const User = mongoose.model('User');
if (username === 'all') {
await User
.find()
.select('+flags +optIn')
.cursor()
.eachAsync(module.sendUserWelcomeEmail, 4);
} else {
const user = await User
.findOne({
username_lc: username.trim().toLowerCase(),
})
.select('+flags +optIn')
.lean();
if (!user) {
throw new Error('user not found');
}
await module.sendUserWelcomeEmail(user);
}
};
module.sendUserWelcomeEmail = async (user) => {
const { user: userService } = module.services;
if (user.flags.isEmailVerified) {
module.log.alert('user is email-verified (aboring welcome email)', {
user: user._id,
username: user.username,
});
return;
}
module.log.info('sending welcome email', {
user: user._id,
username: user.username,
email: user.email,
});
await userService.sendWelcomeEmail(user);
};
module.requestConstellationConnect = async (host) => {
const { coreNode: coreNodeService } = module.services;
const { core, networkPolicy } = await coreNodeService.resolveCore(host);
module.log.info('Constellation Connect is not implemented', { core, networkPolicy });
};
module.requestCoreConnect = async (host) => {
const { coreNode: coreNodeService } = module.services;
const { core, networkPolicy } = await coreNodeService.resolveCore(host);
module.log.info('sending Core connection request', { core, networkPolicy });
const txConnect = await coreNodeService.sendRequest(core, {
method: 'POST',
url: '/core/connect',
body: {
version: module.pkg.version,
site: module.config.site,
},
});
module.log.info('connect tranaction', { txConnect });
};
/*
* SERVER INIT
*/
(async ( ) => {
const argv = require('argv'); // https://www.npmjs.com/package/argv
argv.version(module.pkg.version);
argv.option([
{
name: 'action',
short: 'a',
type: 'string',
description: 'The action to perform',
example: 'sites-cli --action=grant --permission=moderator email email ...',
},
{
name: 'permission',
short: 'p',
type: 'string',
description: 'The permission(s) being added or removed',
example: 'sites-cli --action=grant --permission=admin email email ...'
},
]);
try {
await SitePlatform.startPlatform(module);
} catch (error) {
module.log.error(`failed to start DTP ${module.config.component.slug} platform`, { error });
return;
}
try {
module.app = argv.run();
await SiteAsync.each(module.app.targets, async (target) => {
module.log.info('processing target', { target });
switch (module.app.options.action) {
case 'grant':
await module.grantPermission(target, module.app.options.permission);
break;
case 'revoke':
await module.revokePermission(target, module.app.options.permission);
break;
case 'make-bucket':
await module.makeBucket(target);
break;
case 'core-connect':
await module.requestCoreConnect(target);
break;
case 'constellation-connect':
await module.requestConstellationConnect(target);
break;
case 'delete-otp':
await module.deleteOtpAccount(target);
break;
case 'reset-password':
await module.resetPassword(target);
break;
case 'reset-indexes':
await module.resetIndexes();
break;
case 'welcome-email':
await module.sendWelcomeEmail(target);
break;
default:
throw new Error(`invalid action: ${module.app.options.action}`);
}
});
process.exit(0);
} catch (error) {
module.log.error('failed to process target', { app: module.app, error });
}
})();