Submoduling BotanSS, Reg Draft

This commit is contained in:
2016-02-12 00:16:42 +08:00
commit 0f2ca2e162
17 changed files with 715 additions and 0 deletions

64
notify-term/Notis.js Normal file
View 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
View 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
View 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
View 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();