Submoduling BotanSS, Reg Draft
This commit is contained in:
commit
0f2ca2e162
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
auth.js
|
||||
node_modules
|
||||
*.swp
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "botanss"]
|
||||
path = botanss
|
||||
url = AstroGit:BotanSS.git
|
1
botanss
Submodule
1
botanss
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit a83d3d9469844e6530d56382c97c64d3386ea965
|
1
config/global.js
Normal file
1
config/global.js
Normal file
@ -0,0 +1 @@
|
||||
global.debug = true;
|
6
config/log.js
Normal file
6
config/log.js
Normal file
@ -0,0 +1,6 @@
|
||||
var fs = require( "fs" );
|
||||
var path = "logs/access.log";
|
||||
|
||||
module.exports = {
|
||||
handler: { write: function( e ) { process.stdout.write( e ); } }
|
||||
};
|
68
index.js
Normal file
68
index.js
Normal file
@ -0,0 +1,68 @@
|
||||
require( "./botanss/package" );
|
||||
require( "./config/global" );
|
||||
|
||||
var cl = global.botanLoader;
|
||||
|
||||
var Dragonfly = cl.load( "botanss.Dragonfly" );
|
||||
Dragonfly.defaultSphere = 999;
|
||||
|
||||
|
||||
var cluster = require('cluster');
|
||||
var numCPUs = 2;
|
||||
|
||||
|
||||
if( cluster.isMaster )
|
||||
{
|
||||
var clog = require( "./config/log" );
|
||||
var Masterfly = new Dragonfly( clog.handler );
|
||||
|
||||
var procFock = function( c )
|
||||
{
|
||||
// fork and bind the message bus from masterfly
|
||||
c.fork().addListener( "message", Masterfly.messageBus );
|
||||
};
|
||||
|
||||
var clusterDisconnect = function( worker )
|
||||
{
|
||||
if( worker.suicide === true )
|
||||
{
|
||||
Masterfly.Info( "Worker committed suicide" );
|
||||
Masterfly.Info( "Forking process ..." );
|
||||
procFock( cluster );
|
||||
}
|
||||
else
|
||||
{
|
||||
Masterfly.Info( "Worker died" );
|
||||
}
|
||||
};
|
||||
|
||||
for( var i = 0; i < numCPUs; i ++ ) procFock( cluster );
|
||||
|
||||
cluster.addListener( "disconnect", clusterDisconnect );
|
||||
}
|
||||
else
|
||||
{
|
||||
Dragonfly = new Dragonfly();
|
||||
global.Dragonfly = Dragonfly;
|
||||
|
||||
GLOBAL.X_SERVER_CLUSTER = cluster;
|
||||
|
||||
var AppDomain = cl.load( "botanss.net.AppDomain" );
|
||||
var Httph = cl.load( "botanss.net.Http" );
|
||||
|
||||
//* Host App
|
||||
var WebFrame = cl.load( "botanss.net.WebFrame" );
|
||||
|
||||
// Define AppNS
|
||||
cl.rootNS( "notifyterm", "./notify-term" );
|
||||
cl.rootNS( "notifysrv", "./notifysrv" );
|
||||
|
||||
var App = cl.load( "notifyterm.app" );
|
||||
|
||||
new AppDomain( function( req, res )
|
||||
{
|
||||
var h = new Httph( req, res );
|
||||
new App( h ).run();
|
||||
}, 5000 );
|
||||
//*/
|
||||
}
|
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();
|
15
notifysrv/eventargs/eventargs.js
Normal file
15
notifysrv/eventargs/eventargs.js
Normal file
@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
var cl = global.botanLoader;
|
||||
var Dragonfly = global.Dragonfly;
|
||||
var qstr = cl.load( "notifysrv.utils.querystr" );
|
||||
|
||||
class EventArgs
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.Handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventArgs;
|
23
notifysrv/eventargs/postrequest.js
Normal file
23
notifysrv/eventargs/postrequest.js
Normal file
@ -0,0 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
var cl = global.botanLoader;
|
||||
var Dragonfly = global.Dragonfly;
|
||||
var qstr = cl.load( "notifysrv.utils.querystr" );
|
||||
|
||||
var EventArgs = cl.load( "notifysrv.eventargs.eventargs" );
|
||||
|
||||
class PostRequestEventArgs extends EventArgs
|
||||
{
|
||||
constructor( QueryString )
|
||||
{
|
||||
super();
|
||||
this.Raw = QueryString;
|
||||
}
|
||||
|
||||
get Data()
|
||||
{
|
||||
return qstr.queryStr( this.Raw );
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PostRequestEventArgs;
|
91
notifysrv/postframe.js
Normal file
91
notifysrv/postframe.js
Normal file
@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
|
||||
var cl = global.botanLoader;
|
||||
var Dragonfly = global.Dragonfly;
|
||||
|
||||
var PostRequestEventArgs = cl.load( "notifysrv.eventargs.postrequest" );
|
||||
var EventEmitter = require( "events" ).EventEmitter;
|
||||
|
||||
class PostFrame extends EventEmitter
|
||||
{
|
||||
constructor( Http )
|
||||
{
|
||||
super();
|
||||
this.HTTP = Http;
|
||||
this.result = "Error: PostFrame is unhandled";
|
||||
this.planted = false;
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
var _self = this;
|
||||
var requestStr = "";
|
||||
|
||||
if( this.HTTP.request.isPost )
|
||||
{
|
||||
var Req = this.HTTP.request.raw;
|
||||
|
||||
var ReceiveData = function( data )
|
||||
{
|
||||
requestStr += data + "";
|
||||
if( 51200 < requestStr.length )
|
||||
{
|
||||
_self.result = "The size of request is too big ( 500KB < )";
|
||||
Req.removeListener( "data", ReceiveData );
|
||||
Req.removeListener( "end", ReceiveEnd );
|
||||
|
||||
_self.plantResult();
|
||||
}
|
||||
};
|
||||
|
||||
var ReceiveEnd = function()
|
||||
{
|
||||
var EventArgs = new PostRequestEventArgs( requestStr );
|
||||
|
||||
_self.emit( "PostRequest", this, EventArgs );
|
||||
if( !EventArgs.Handled )
|
||||
{
|
||||
_self.result = "Error: Unhandled Request";
|
||||
_self.plantResult();
|
||||
}
|
||||
};
|
||||
|
||||
Req.addListener( "data", ReceiveData );
|
||||
Req.addListener( "end", ReceiveEnd );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Dragonfly.Info(
|
||||
"GET: " + encodeURI( this.HTTP.request.raw.url )
|
||||
+ " - " + this.HTTP.request.raw.headers["user-agent"]
|
||||
, Dragonfly.Visibility.VISIBLE
|
||||
);
|
||||
}
|
||||
|
||||
this.plantResult();
|
||||
}
|
||||
|
||||
plantResult()
|
||||
{
|
||||
if( !this.planted )
|
||||
{
|
||||
this.planted = true;
|
||||
if( this.HTTP )
|
||||
{
|
||||
if( !( this.result instanceof Buffer ) )
|
||||
{
|
||||
this.result = String( this.result );
|
||||
}
|
||||
|
||||
this.HTTP.response.headers["Content-Type"] = "text/plain";
|
||||
this.HTTP.response.headers["Content-Length"] = this.result.length;
|
||||
this.HTTP.response.write( this.result );
|
||||
this.HTTP.response.end();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PostFrame;
|
14
notifysrv/utils/querystr.js
Normal file
14
notifysrv/utils/querystr.js
Normal file
@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
queryStr: function( qstr )
|
||||
{
|
||||
var qObj = {};
|
||||
|
||||
qstr.split( "&" ).forEach( function( val )
|
||||
{
|
||||
val = val.split( "=" );
|
||||
qObj[ val[0] ] = val[1] ? decodeURIComponent( val[1].replace( /\+/g, " " ) ) : true;
|
||||
} );
|
||||
|
||||
return qObj;
|
||||
}
|
||||
};
|
15
notifysrv/utils/random.js
Normal file
15
notifysrv/utils/random.js
Normal file
@ -0,0 +1,15 @@
|
||||
var lut = []; for ( var i=0; i<256; i++ ) { lut[i] = (i<16?'0':'')+(i).toString(16); }
|
||||
|
||||
module.exports = {
|
||||
uuid: function()
|
||||
{
|
||||
var d0 = Math.random()*0xffffffff|0;
|
||||
var d1 = Math.random()*0xffffffff|0;
|
||||
var d2 = Math.random()*0xffffffff|0;
|
||||
var d3 = Math.random()*0xffffffff|0;
|
||||
return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
|
||||
lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
|
||||
lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
|
||||
lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
|
||||
}
|
||||
};
|
13
notifysrv/utils/string.js
Normal file
13
notifysrv/utils/string.js
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
encodeHtml: function ( str, br )
|
||||
{
|
||||
str = ( str + "" ).replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
;
|
||||
if( br ) str = str.replace( /\n/g, "<br/>" );
|
||||
return str;
|
||||
}
|
||||
}
|
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "notify-term"
|
||||
, "exportName": "notify-term"
|
||||
, "version": "0.0.1"
|
||||
, "dependencies": {
|
||||
"mongoose": "latest"
|
||||
}
|
||||
, "description": "The notify-term push notification server"
|
||||
, "main": "index.js"
|
||||
, "scripts": {
|
||||
}
|
||||
, "keywords": [
|
||||
"notification"
|
||||
, "terminal"
|
||||
, "notifier"
|
||||
]
|
||||
, "author": "Penguin <tgckpg@gmail.com>"
|
||||
, "license": "MIT"
|
||||
, "main": "index.js"
|
||||
}
|
Loading…
Reference in New Issue
Block a user