// dtp-webapp-cli.js // Copryright (C) 2022 DTP Technologies, LLC // License: Apache-2.0 'use strict'; const DTP_COMPONENT = { name: 'Sample App CLI', slug: 'webapp-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: 'dtp-webapp-cli --action=grant --permission=moderator email email ...', }, { name: 'permission', short: 'p', type: 'string', description: 'The permission(s) being added or removed', example: 'dtp-webapp-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 }); } })();