'use strict' ; import http from 'http' ; import https from 'https' ; import os from 'os' ; import path from 'path' ; import { v4 as uuidv4 } from 'uuid' ; import crypto from 'crypto' ; import fsPromises from 'fs/promises' ; import { createTransport } from 'nodemailer' ; import { Logger } from '@Tools/Logging' ; import { VKeyedCollection } from '@Tools/vTypes' ; import { Config } from '@Base/config' ; import { AccountEntity } from '@Entities/AccountEntity' ; export function Clamp ( pVal , pLow , pHigh ) { let ret = pVal ; if ( ret > pHigh ) ret = pHigh ; if ( ret < pLow ) ret = pLow ; return ret ; } export function IsNullOrEmpty ( pVal ) { return ( typeof ( pVal ) === 'undefined' ) || ( pVal === null ) || ( typeof ( pVal ) === 'string' && String ( pVal ) . length === 0 ) ; } ; export function IsNotNullOrEmpty ( pVal ) { return ! IsNullOrEmpty ( pVal ) ; } ; export function SimpleObject ( pProperty , pValue ) { const ret = { } ; ret [ pProperty ] = pValue ; return ret ; } ; export function ParseQueryString ( pQuery ) { const ret = new Map ( ) ; const args = decodeURI ( pQuery ) . split ( '&' ) ; args . forEach ( arg => { const argPieces = arg . split ( '=' ) ; switch ( argPieces . length ) { case 1 : ret . set ( argPieces [ 0 ] , null ) ; break ; case 2 : ret . set ( argPieces [ 0 ] , argPieces [ 1 ] ) ; break ; default : break ; } ; } ) ; return ret ; } ; export function GenUUID ( ) { return uuidv4 ( ) ; } ; export function genRandomString ( pLen ) { return crypto . randomBytes ( Math . ceil ( pLen / 2 ) ) . toString ( 'hex' ) . slice ( 0 , pLen ) ; } ; let myExternalAddr ; export async function getMyExternalIPAddress ( ) { if ( IsNotNullOrEmpty ( myExternalAddr ) ) { return Promise . resolve ( myExternalAddr ) ; } ; return new Promise ( ( resolve , reject ) => { httpsRequest ( 'https://api.ipify.org' ) . then ( resp => { myExternalAddr = resp ; resolve ( myExternalAddr ) ; } ) . catch ( err => { const networkInterfaces = os . networkInterfaces ( ) ; let ret = ( ) => [ ] . concat ( ... Object . values ( networkInterfaces ) ) . filter ( details => details . family === 'IPv4' && ! details . internal ) . pop ( ) . address ; if ( IsNullOrEmpty ( ret ) ) { ret = ( ) => [ ] . concat ( ... Object . values ( networkInterfaces ) ) . filter ( details => details . family === 'IPv6' && ! details . internal ) . pop ( ) . address ; } Logger . debug ( `getMyExternalIPAddress: resolved interface addr = ${ ret }` ) ; if ( IsNullOrEmpty ( ret ) ) { reject ( 'No address found' ) ; } myExternalAddr = ret . toString ( ) ; resolve ( myExternalAddr ) ; } ) ; } ) ; } ; export async function httpRequest ( pUrl ) { return new Promise ( ( resolve , reject ) => { http . get ( pUrl , ( resp ) => { let data = '' ; resp . on ( 'data' , ( chunk ) => { data += chunk ; } ) ; resp . on ( 'end' , ( ) => { resolve ( data ) ; } ) ; } ) . on ( 'error' , ( err ) => { reject ( err ) ; } ) ; } ) ; } ; export async function httpsRequest ( pUrl ) { return new Promise ( ( resolve , reject ) => { https . get ( pUrl , ( resp ) => { let data = '' ; resp . on ( 'data' , ( chunk ) => { data += chunk ; } ) ; resp . on ( 'end' , ( ) => { resolve ( data ) ; } ) ; } ) . on ( 'error' , ( err ) => { reject ( err ) ; } ) ; } ) ; } ; export async function SendVerificationEmail ( pAccount , pVerifyCode ) { try { const verificationURL = Config . metaverse [ 'metaverse-server-url' ] + `/api/v1/account/verify/email?a=${ pAccount . id }&v=${ pVerifyCode }` ; const metaverseName = Config . metaverse [ 'metaverse-name' ] ; const shortMetaverseName = Config . metaverse [ 'metaverse-nick-name' ] ; const verificationFile = path . join ( __dirname , '../..' , Config [ 'metaverse-server' ] [ 'email-verification-email-body' ] ) ; Logger . debug ( `SendVerificationEmail: using verificationFile from ${ verificationFile }` ) ; let emailBody = await fsPromises . readFile ( verificationFile , 'utf-8' ) ; emailBody = emailBody . replace ( 'VERIFICATION_URL' , verificationURL ) . replace ( 'METAVERSE_NAME' , metaverseName ) . replace ( 'SHORT_METAVERSE_NAME' , shortMetaverseName ) ; Logger . debug ( `SendVerificationEmail: SMTPhost=${ Config [ 'nodemailer-transport-config' ] . host }` ) ; const transporter = createTransport ( Config [ 'nodemailer-transport-config' ] ) ; if ( transporter ) { Logger . debug ( `SendVerificationEmail: sending email verification for new account ${ pAccount . id }/${ pAccount . username }` ) ; const msg = { from : Config [ 'metaverse-server' ] [ 'email-verification-from' ] , to : pAccount . email , subject : `${ shortMetaverseName } account verification` , html : emailBody } ; transporter . sendMail ( msg ) ; transporter . close ( ) ; } else { Logger . error ( `SendVerificationEmail: failed to recreate transporter` ) ; } ; } catch ( e ) { Logger . error ( `SendVerificationEmail: exception sending verification email. Acct=${ pAccount . id }/${ pAccount . username }. e=${ e }` ) ; } return ; }