Submoduling BotanSS, Reg Draft
This commit is contained in:
		
							
								
								
									
										64
									
								
								notify-term/Notis.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								notify-term/Notis.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
var cl = global.botanLoader;
 | 
			
		||||
var Dragonfly = global.Dragonfly;
 | 
			
		||||
 | 
			
		||||
var HttpRequest = cl.load( "botanss.net.HttpRequest" );
 | 
			
		||||
var ustr = cl.load( "notifysrv.utils.string" );
 | 
			
		||||
 | 
			
		||||
class Notis 
 | 
			
		||||
{
 | 
			
		||||
	constructor( query )
 | 
			
		||||
	{
 | 
			
		||||
		this.__valid = false;
 | 
			
		||||
		this.__error = null;
 | 
			
		||||
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			if( !query.id )
 | 
			
		||||
			{
 | 
			
		||||
				throw new Error( "ID is not specified" );
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.id = query.id;
 | 
			
		||||
 | 
			
		||||
			if( query.xml )
 | 
			
		||||
			{
 | 
			
		||||
				this.Xml = query.xml;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				this.Xml = Notis.ToastText02
 | 
			
		||||
					.replace( "{TITLE}", ustr.encodeHtml( query.title ) )
 | 
			
		||||
					.replace( "{MESG}", ustr.encodeHtml( query.message ) );
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.__valid = true;
 | 
			
		||||
		}
 | 
			
		||||
		catch( ex )
 | 
			
		||||
		{
 | 
			
		||||
			Dragonfly.Error( ex );
 | 
			
		||||
			this.__error = ex.message;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get Valid() { return this.__valid; }
 | 
			
		||||
	get Error() { return this.__error; }
 | 
			
		||||
 | 
			
		||||
	static get ToastText02()
 | 
			
		||||
	{
 | 
			
		||||
		return `
 | 
			
		||||
<toast>
 | 
			
		||||
	<visual>
 | 
			
		||||
		<binding template="ToastText02">
 | 
			
		||||
			<text id="1">{TITLE}</text>
 | 
			
		||||
			<text id="2">{MESG}</text>
 | 
			
		||||
		</binding>
 | 
			
		||||
	</visual>
 | 
			
		||||
</toast>
 | 
			
		||||
`;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Notis;
 | 
			
		||||
							
								
								
									
										208
									
								
								notify-term/WNSAuth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								notify-term/WNSAuth.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
var cl = global.botanLoader;
 | 
			
		||||
var Dragonfly = global.Dragonfly;
 | 
			
		||||
 | 
			
		||||
var EventEmitter = require( "events" ).EventEmitter;
 | 
			
		||||
 | 
			
		||||
var HttpRequest = cl.load( "botanss.net.HttpRequest" );
 | 
			
		||||
var Rand = cl.load( "notifysrv.utils.random" );
 | 
			
		||||
var Notis = cl.load( "notifyterm.Notis" );
 | 
			
		||||
var Model = cl.load( "notifyterm.schema" );
 | 
			
		||||
 | 
			
		||||
// private static var
 | 
			
		||||
var AuthTokenName = "WNSAuthToken";
 | 
			
		||||
var AuthToken = false;
 | 
			
		||||
 | 
			
		||||
class WNSAuth extends EventEmitter
 | 
			
		||||
{
 | 
			
		||||
	constructor()
 | 
			
		||||
	{
 | 
			
		||||
		super();
 | 
			
		||||
		this.__inAuth = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get IsAuthenticated() { return Boolean( AuthToken ); }
 | 
			
		||||
 | 
			
		||||
	Authenticate()
 | 
			
		||||
	{
 | 
			
		||||
		if( this.IsAuthenticated )
 | 
			
		||||
		{
 | 
			
		||||
			this.__emitAuthComplete();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if( this.__inAuth ) return;
 | 
			
		||||
 | 
			
		||||
		this.__inAuth = true;
 | 
			
		||||
		var _self = this;
 | 
			
		||||
 | 
			
		||||
		Model.Tokens.findOne({ name: AuthTokenName })
 | 
			
		||||
		.exec( ( err, data ) => {
 | 
			
		||||
			if( err || !( data && data.token ) )
 | 
			
		||||
			{
 | 
			
		||||
				Dragonfly.Info( "Database does not contain access token, authenticating" );
 | 
			
		||||
				_self.__authWNS();
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				Dragonfly.Info( "Access token found in database, using it" );
 | 
			
		||||
				AuthToken = data.token;
 | 
			
		||||
				_self.__emitAuthComplete();
 | 
			
		||||
			}
 | 
			
		||||
		} );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Register( ChannelUri, handler )
 | 
			
		||||
	{
 | 
			
		||||
		var _self = this;
 | 
			
		||||
		var VerifyChannel = () =>
 | 
			
		||||
		{
 | 
			
		||||
			var N = new Notis({
 | 
			
		||||
				id: "Null"
 | 
			
		||||
				, title: "Channel Registration"
 | 
			
		||||
				, message: "Registration success"
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			var uuid = Rand.uuid();
 | 
			
		||||
 | 
			
		||||
			_self.__send( ChannelUri, N, ( sender, e ) => {
 | 
			
		||||
				if( e.statusCode == 200 )
 | 
			
		||||
				{
 | 
			
		||||
					Model.Tokens.update(
 | 
			
		||||
						{ name: uuid }
 | 
			
		||||
						, { name: uuid, token: ChannelUri }
 | 
			
		||||
						, { upsert: true }
 | 
			
		||||
					)
 | 
			
		||||
					.exec( ( err, data ) => {
 | 
			
		||||
						if( err )
 | 
			
		||||
						{
 | 
			
		||||
							Dragonfly.Error( err );
 | 
			
		||||
							handler( _self, "Server Error: Cannot save channel information" );
 | 
			
		||||
							return;
 | 
			
		||||
						}
 | 
			
		||||
						handler( _self, uuid );
 | 
			
		||||
					} );
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				handler( _self, e.statusCode + " Server Error: Unable to push message to channel" );
 | 
			
		||||
 | 
			
		||||
			} );
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		if( !this.Authenticated )
 | 
			
		||||
		{
 | 
			
		||||
			this.once( "AuthComplete", VerifyChannel );
 | 
			
		||||
			this.Authenticate();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			VerifyChannel();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Deliver( NotisQ )
 | 
			
		||||
	{
 | 
			
		||||
		Model.Tokens
 | 
			
		||||
		.findOne({ name: NotisQ.id })
 | 
			
		||||
		.exec( ( err, data ) => {
 | 
			
		||||
			if( err )
 | 
			
		||||
			{
 | 
			
		||||
				Dragonfly.Error( err );
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if( data && data.token )
 | 
			
		||||
			{
 | 
			
		||||
				this.__send( data.token, NotisQ, ( sender, e ) => {
 | 
			
		||||
					Dragonfly.Debug( e.Data );
 | 
			
		||||
				} );
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				Dragonfly.Info( "Channel not found: " + NotisQ.id );
 | 
			
		||||
			}
 | 
			
		||||
		} );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__send( ChannelUri, NotisQ, handler )
 | 
			
		||||
	{
 | 
			
		||||
		if( !ChannelUri )
 | 
			
		||||
		{
 | 
			
		||||
			handler( this, "Channel is undefined" );
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var Request = new HttpRequest( ChannelUri, {
 | 
			
		||||
			"Authorization":  "Bearer " + AuthToken
 | 
			
		||||
			, "X-WNS-RequestForStatus": "true"
 | 
			
		||||
			, "X-WNS-Type": "wns/toast"
 | 
			
		||||
		} );
 | 
			
		||||
 | 
			
		||||
		if( !Request.Hostname.match( /.*\.notify\.windows\.com$/ ) )
 | 
			
		||||
		{
 | 
			
		||||
			handler( this, "Malicious hostname: " + Request.Hostname );
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Request.PostData( NotisQ.Xml );
 | 
			
		||||
		Request.Headers[ "Content-Type" ] = "text/xml";
 | 
			
		||||
 | 
			
		||||
		Request.addListener( "RequestComplete", handler );
 | 
			
		||||
 | 
			
		||||
		Request.Send();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__authWNS()
 | 
			
		||||
	{
 | 
			
		||||
		var serviceAuth = cl.load( "notifyterm.config.auth" );
 | 
			
		||||
 | 
			
		||||
		var Request = new HttpRequest( serviceAuth.Uri );
 | 
			
		||||
 | 
			
		||||
		Request.PostData(
 | 
			
		||||
			"grant_type=client_credentials"
 | 
			
		||||
			+ "&client_id=" + serviceAuth.Id 
 | 
			
		||||
			+ "&client_secret=" + encodeURIComponent( serviceAuth.Secret )
 | 
			
		||||
			+ "&scope=notify.windows.com"
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		Request.addListener( "RequestComplete", this.__requestComplete.bind( this ) );
 | 
			
		||||
 | 
			
		||||
		Request.Send();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__requestComplete( sender, e )
 | 
			
		||||
	{
 | 
			
		||||
		var _self = this;
 | 
			
		||||
		let JResponse = JSON.parse( e.ResponseString );
 | 
			
		||||
 | 
			
		||||
		if( JResponse && JResponse.access_token )
 | 
			
		||||
		{
 | 
			
		||||
			AuthToken = JResponse.access_token;
 | 
			
		||||
			Dragonfly.Info( "Authorization Success" );
 | 
			
		||||
 | 
			
		||||
			Model.Tokens
 | 
			
		||||
				.update(
 | 
			
		||||
					{ name: AuthTokenName }
 | 
			
		||||
					, { name: AuthTokenName, token: AuthToken }
 | 
			
		||||
					, { upsert: true }
 | 
			
		||||
				)
 | 
			
		||||
				.exec( ( err, data ) => _self.__emitAuthComplete() );
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			Dragonfly.Error( "Unable to authenticate: " + e.ResponseString );
 | 
			
		||||
			_self.__emitAuthComplete();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__emitAuthComplete()
 | 
			
		||||
	{
 | 
			
		||||
		this.__inAuth = false;
 | 
			
		||||
		this.emit( "AuthComplete", this );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = WNSAuth;
 | 
			
		||||
							
								
								
									
										93
									
								
								notify-term/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								notify-term/app.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
var cl = global.botanLoader;
 | 
			
		||||
var Dragonfly = global.Dragonfly;
 | 
			
		||||
 | 
			
		||||
var HttpRequest = cl.load( "botanss.net.HttpRequest" );
 | 
			
		||||
var Base = cl.load( "notifysrv.postframe" );
 | 
			
		||||
 | 
			
		||||
var WNSAuth = cl.load( "notifyterm.WNSAuth" );
 | 
			
		||||
var Model = cl.load( "notifyterm.schema" );
 | 
			
		||||
var NotisQ = cl.load( "notifyterm.Notis" );
 | 
			
		||||
 | 
			
		||||
class App extends Base
 | 
			
		||||
{
 | 
			
		||||
	constructor( Http )
 | 
			
		||||
	{
 | 
			
		||||
		super( Http );
 | 
			
		||||
		this.result = "Hello there! This is a notify-term server.\nFor more information please head to https://github.com/tgckpg/notify-term";
 | 
			
		||||
 | 
			
		||||
		this.OAuth = new WNSAuth();
 | 
			
		||||
		this.OAuth.addListener( "AuthComplete", this.OnAuthed.bind( this ) );
 | 
			
		||||
		this.OAuth.Authenticate();
 | 
			
		||||
		this.RequestQueue = [];
 | 
			
		||||
 | 
			
		||||
		this.addListener( "PostRequest", this.PostRequest );
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PostRequest( sender, e )
 | 
			
		||||
	{
 | 
			
		||||
		e.Handled = true;
 | 
			
		||||
 | 
			
		||||
		var _self = this;
 | 
			
		||||
		var query = e.Data;
 | 
			
		||||
 | 
			
		||||
		switch( query.action )
 | 
			
		||||
		{
 | 
			
		||||
			case "register":
 | 
			
		||||
				this.OAuth.Register(
 | 
			
		||||
				query.uri, ( sender, mesg ) => {
 | 
			
		||||
					_self.result = mesg;
 | 
			
		||||
					_self.plantResult();
 | 
			
		||||
				} );
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case "deliver":
 | 
			
		||||
				this.__sendMesg( query );
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				this.result = "Invalid Action";
 | 
			
		||||
				this.plantResult();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	__sendMesg( query )
 | 
			
		||||
	{
 | 
			
		||||
		var N = new NotisQ( query );
 | 
			
		||||
		if( N.Valid )
 | 
			
		||||
		{
 | 
			
		||||
			this.RequestQueue.push( N );
 | 
			
		||||
			this.result = "Your message has been queued";
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			this.result = "Invalid message format";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.plantResult();
 | 
			
		||||
		this.OAuth.Authenticate();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	OnAuthed( OAuth )
 | 
			
		||||
	{
 | 
			
		||||
		if( !OAuth.IsAuthenticated )
 | 
			
		||||
		{
 | 
			
		||||
			Dragonfly.Error( "Unable to authenticate" );
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.ProcessQueue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ProcessQueue()
 | 
			
		||||
	{
 | 
			
		||||
		if( !this.RequestQueue.length ) return;
 | 
			
		||||
 | 
			
		||||
		var Request = this.RequestQueue.shift();
 | 
			
		||||
 | 
			
		||||
		Dragonfly.Info( "Processing Request: " + Request );
 | 
			
		||||
 | 
			
		||||
		this.OAuth.Deliver( Request );
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = App;
 | 
			
		||||
							
								
								
									
										77
									
								
								notify-term/schema.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								notify-term/schema.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
var Dragonfly = global.Dragonfly;
 | 
			
		||||
var cl = global.botanLoader; 
 | 
			
		||||
var util = require( "util" );
 | 
			
		||||
var events = require( "events" );
 | 
			
		||||
var mongoose = require( "mongoose" );
 | 
			
		||||
	var Schema = mongoose.Schema;
 | 
			
		||||
 | 
			
		||||
var options = cl.load( "notifyterm.config.db" );
 | 
			
		||||
 | 
			
		||||
var throwEverything = function( err ) {
 | 
			
		||||
	if( err ) {
 | 
			
		||||
		throw err;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var db = mongoose.connection;
 | 
			
		||||
db.on( "error", throwEverything );
 | 
			
		||||
 | 
			
		||||
mongoose.connect( options.host, options.auth );
 | 
			
		||||
 | 
			
		||||
/* Schema Heads */
 | 
			
		||||
var R_Tokens = { type: Schema.Types.ObjectId, ref: "Tokens" };
 | 
			
		||||
/* End Schema Heads */
 | 
			
		||||
 | 
			
		||||
var M_Tokens = new Schema({
 | 
			
		||||
	name: { type: String, unique: true }
 | 
			
		||||
	, token: { type: String }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var DB = function ()
 | 
			
		||||
{
 | 
			
		||||
	events.EventEmitter.call( this );
 | 
			
		||||
	var Models = [
 | 
			
		||||
		  { name: "Tokens" , schema: M_Tokens , hasKey: true }
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
	var l = Models.length;
 | 
			
		||||
 | 
			
		||||
	var _widx = 0;
 | 
			
		||||
	var _widxl = 0;
 | 
			
		||||
 | 
			
		||||
	var _self = this;
 | 
			
		||||
 | 
			
		||||
	var ready = function()
 | 
			
		||||
	{
 | 
			
		||||
		_self.ready = true;
 | 
			
		||||
		_self.emit( "ready", _self );
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	var waitIndex = function( err )
 | 
			
		||||
	{
 | 
			
		||||
		_widx ++;
 | 
			
		||||
		throwEverything( err );
 | 
			
		||||
		if( _widx == _widxl ) ready();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	this.ready = false;
 | 
			
		||||
 | 
			
		||||
	for( var i = 0; i < l; i ++ )
 | 
			
		||||
	{
 | 
			
		||||
		var mod = Models[i];
 | 
			
		||||
		var b = mongoose.model( mod.name, mod.schema );
 | 
			
		||||
		if( mod.hasKey )
 | 
			
		||||
		{
 | 
			
		||||
			_widxl ++;
 | 
			
		||||
			b.on( "index", waitIndex );
 | 
			
		||||
		}
 | 
			
		||||
		this[ mod.name ] = b;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if( !_widxl ) ready();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
util.inherits( DB, events.EventEmitter );
 | 
			
		||||
 | 
			
		||||
module.exports = new DB();
 | 
			
		||||
		Reference in New Issue
	
	Block a user