123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- // persistence.js
- // Helper functions for VWF node server that handles all persitence related requests
- var helpers = require( './helpers' ),
- serve = require( './serve' ),
- storage = require( './storagefs' ),
- fs = require( 'fs' ),
- url = require( 'url' ),
- libpath = require( 'path' ),
- querystring = require( 'querystring' );
- function CreateSaveDirectory( application_path, save_name, save_revision ) {
- var application_segments = helpers.GenerateSegments( application_path );
- var current_directory = "./documents";
- while ( application_segments.length > 0 ) {
- current_directory = helpers.JoinPath( current_directory, application_segments.shift() );
- if ( ! helpers.IsDirectory( current_directory ) ) {
- fs.mkdirSync( current_directory );
- }
- }
- current_directory = helpers.JoinPath( current_directory, save_name );
- if ( ! helpers.IsDirectory( current_directory ) ) {
- fs.mkdirSync( current_directory );
- }
- }
- // LoadSaveObject function is designed to take the load information provided by the
- // GetLoadInformation function, load and parse the specified file, and return that object
- // (or undefined, if there is no such file ).
- function LoadSaveObject( loadInfo ) {
- if ( loadInfo[ 'save_name' ] ) {
- var fileName = helpers.JoinPath( "./documents", loadInfo[ 'application_path' ], loadInfo[ 'save_name' ], "saveState_" + loadInfo[ 'save_revision' ] + ".vwf.json" );
- if ( helpers.IsFile( fileName ) ) {
- var fileContents = fs.readFileSync( fileName, "utf8" );
- return JSON.parse( fileContents );
- }
- }
- return undefined;
- }
- // LookupSaveRevisions takes the public path and the name of a save, and provides
- // an array of all revisions for that save. (If the save does not exist, this will be
- // an empty array).
- function LookupSaveRevisions( public_path, save_name ) {
- var result = [ ];
- var directoryName = helpers.JoinPath( "./documents/", public_path, save_name );
- if ( helpers.IsDirectory( directoryName ) ) {
- var potentialSaves = fs.readdirSync( directoryName.replace( /\//g, libpath.sep ) );
- for ( var index = 0; index < potentialSaves.length; index++ ) {
- if ( potentialSaves[ index ].match( /^saveState_\d+\.vwf\.json$/ ) ) {
- result.push( parseInt( potentialSaves[ index ].slice( 10, potentialSaves[ index ].length - 9 ) ) );
- }
- }
- }
- return result;
- }
- // GetLoadInformation receives a parsed request {private_path, public_path, instance, application} and returns the
- // details of the save that is designated by the initial request. The details are returned in an object
- // composed of: save_name (name of the save) save_revision (revision of the save), explicit_revision (boolean, true if the request
- // explicitly specified the revision, false if it did not), and application_path (the public_path of the application this is a save for).
- function GetLoadInformation( parsedRequest ) {
- var result = {'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
- if ( parsedRequest[ 'private_path' ] ) {
- var segments = helpers.GenerateSegments( parsedRequest[ 'private_path' ] );
- if ( ( segments.length > 1 ) && ( segments[ 0 ] == "load" ) ) {
- var potentialRevisions = LookupSaveRevisions( parsedRequest[ 'public_path' ], segments[ 1 ] );
- if ( potentialRevisions.length > 0 ) {
- result[ 'save_name' ] = segments[ 1 ];
-
- if ( segments.length > 2 ) {
- var requestedRevision = parseInt( segments[ 2 ] );
- if ( requestedRevision ) {
- if ( potentialRevisions.indexOf( requestedRevision ) > -1 ) {
- result[ 'save_revision' ] = requestedRevision;
- result[ 'explicit_revision' ] = true;
- result[ 'application_path' ] = parsedRequest[ 'public_path' ];
- }
- }
- }
- if ( result[ 'explicit_revision' ] == undefined ) {
- result[ 'explicit_revision' ] = false;
- potentialRevisions.sort( );
- result[ 'save_revision' ] = potentialRevisions.pop();
- result[ 'application_path' ] = parsedRequest[ 'public_path' ];
- }
- }
- }
- }
- return result;
- }
- // GetSaveInformation is a helper function that takes a NodeJS request, a NodeJS response,
- // and the application_path (/path/to/application). It returns an array of all saves found for that
- // application (including separate entries for individual revisions of saves ).
- function GetSaveInformation( request, response, application_path ) {
- var result = [ ];
- var potentialSaveNames = fs.readdirSync( helpers.JoinPath("./documents", application_path ) );
- for ( var index = 0; index < potentialSaveNames.length; index++ ) {
- if ( helpers.IsDirectory( helpers.JoinPath( "./documents", application_path, potentialSaveNames[ index ] ) ) ) {
- var revisionList = LookupSaveRevisions( application_path, potentialSaveNames[ index ] );
- var latestsave = true;
- revisionList.sort();
- while ( revisionList.length > 0 ) {
- var newEntry = {};
- newEntry[ 'applicationpath' ] = application_path;
- newEntry[ 'savename' ] = potentialSaveNames[ index ];
- newEntry[ 'revision' ] = revisionList.pop().toString();
- newEntry[ 'latestsave' ] = latestsave;
- if ( latestsave ) {
- newEntry[ 'url' ] = helpers.JoinPath( "http://" + request.headers.host, application_path, "load", potentialSaveNames[ index ] + "/" );
- }
- else {
- newEntry[ 'url' ] = helpers.JoinPath( "http://" + request.headers.host, application_path, "load", potentialSaveNames[ index ] + "/", newEntry[ 'revision' ] + "/" );
- }
- latestsave = false;
- result.push( newEntry );
- }
- }
- }
- return result;
- }
- // GetSaveInformationRecursive is a helper function that recursively calls the
- // GetSaveInformation function on a directory, and all directories within it (and within those, and so on).
- function GetSaveInformationRecursive( request, response, application_path ) {
- var result = [];
- var subDirectories = fs.readdirSync( helpers.JoinPath("./documents", application_path ) );
- for ( var index = 0; index < subDirectories.length; index++ ) {
- if ( helpers.IsDirectory( helpers.JoinPath( "./documents", application_path, subDirectories[ index ] ) ) ) {
- result = result.concat( GetSaveInformationRecursive( request, response, helpers.JoinPath( application_path, subDirectories[ index ] ) ) );
- }
- }
- result = result.concat( GetSaveInformation( request, response, application_path ) );
- return result;
- }
- // HandlePersistenceLoad attempts to deal with a request which contains load arguments in the
- // private_path.
- // If the URL is properly formed, it will either (if the load portion of the URL is the last
- // portion of the URL, and there is no trailing slash) redirect to the same URL with a trailing
- // slash added, or if the URL is otherwise properly formed, it will remove the load specific
- // portions of the private_path, and pass this updated parsedRequest on to the normal application
- // level Serve function (effectively, load URL's are treated as if all load specific portions
- // of the URL don't exist, except for the websocket connection, which uses it to load the initial
- // state for the first client. ).
- function HandlePersistenceLoad( request, response, parsedRequest, segments ) {
- var loadInformation = GetLoadInformation( parsedRequest );
- if ( loadInformation[ 'save_name' ] != undefined ) {
- segments.shift();
- segments.shift();
- if ( loadInformation[ 'explicit_revision' ] ) {
- segments.shift();
- }
- var subParsedRequest = { };
- subParsedRequest[ 'public_path' ] = parsedRequest[ 'public_path' ];
- subParsedRequest[ 'application' ] = parsedRequest[ 'application' ];
- if ( segments.length > 0 ) {
- subParsedRequest[ 'private_path' ] = segments.join("/");
- }
- else {
- subParsedRequest[ 'private_path' ] = undefined;
- }
- subParsedRequest[ 'instance' ] = parsedRequest[ 'instance' ];
- if ( subParsedRequest[ 'instance' ] ) {
- return global.application.Serve( request, response, subParsedRequest );
- }
- else if ( request.method == "GET" ) {
- var redirectPath = parsedRequest[ 'public_path' ];
- if ( parsedRequest[ 'application' ] ) {
- redirectPath = helpers.JoinPath( redirectPath, parsedRequest[ 'application' ] );
- }
- redirectPath = helpers.JoinPath( redirectPath, helpers.GenerateInstanceID( ), parsedRequest[ 'private_path' ] );
- if ( segments.length == 0 ) {
- redirectPath = helpers.JoinPath( redirectPath, "/");
- }
- serve.Redirect( redirectPath, response );
- return true;
- }
- }
- return false;
- }
- function GenerateSaveObject( request, public_path, application, instance, save_name ) {
- var result = {};
- result[ "name" ] = save_name;
- result[ "url" ] = helpers.JoinPath( "http://" + request.headers.host, public_path, application, instance, "saves", save_name );
- result[ "vwf_info" ] = {};
- result[ "vwf_info" ][ "public_path" ] = public_path;
- result[ "vwf_info" ][ "application" ] = application;
- result[ "vwf_info" ][ "path_to_application" ] = helpers.JoinPath( public_path, application );
- result[ "vwf_info" ][ "instance" ] = instance;
- var metadata = storage.GetSaveMetadata( public_path, application, instance, save_name );
- if ( metadata ) {
- result[ "metadata" ] = metadata;
- }
- else {
- result[ "metadata" ] = {}
- }
- return result;
-
- }
- function HandleSavesLoad( request, response, parsedRequest, segments ) {
- // Saves are listed/loaded via GET, and saved via POST
- if ( request.method == "GET" ) {
- //Dealing with either loading a save state, or listing save states,
- // or a malformed request.
- if ( segments.length == 1 ) {
- //No arguments beyond saves, must be listing save states.
- // If instance was set, listing saves for that instance,
- // otherwise listing saves for all instances of this application.
- if ( parsedRequest[ 'instance' ] ) {
- //Listing for specific instance.
- var save_name_list = storage.ListInstanceSaveStates( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], parsedRequest[ 'instance' ] );
- var save_objects = [ ];
- for ( var index = 0; index < save_name_list.length; index++ ) {
- save_objects.push( GenerateSaveObject( request, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], parsedRequest[ 'instance' ], save_name_list[ index ] ) );
- }
- serve.JSON( save_objects, response, url.parse( request.url, true ) );
- return true;
- }
- else {
- var save_hash = storage.ListApplicationSaveStates( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ] );
- var instance_hash = { };
- for ( instance_id in save_hash ) {
- var save_objects = [ ];
- for ( var index = 0; index < save_hash[ instance_id ].length; index++ ) {
- save_objects.push( GenerateSaveObject( request, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], instance_id, save_hash[ instance_id ][ index ] ) );
- }
- instance_hash[ instance_id ] = save_objects;
- }
- serve.JSON( instance_hash, response, url.parse( request.url, true ) );
- return true;
- }
- } else if ( segements.length == 2 ) {
- }
- } else if ( request.method == "POST" ) {
- }
- return false;
- }
- function HandlePersistenceSave( request, response, parsedRequest, segments ) {
- if ( segments.length == 2 ) {
- var saveName = segments[ 1 ];
- var saveRevision = new Date().valueOf();
- CreateSaveDirectory( parsedRequest[ 'public_path' ], saveName, saveRevision );
- var persistenceData = "";
- var persistenceLength = 0;
- request.on( 'data', function ( data ) {
- persistenceData += data;
- if ( persistenceData.length > 50000000 ) {
- persistenceData = "";
- response.writeHead( 413, { 'Content-Type': 'text/plain' } ).end( );
- request.connection.destroy();
- }
- } );
- request.on( 'end', function( ) {
- if ( persistenceData.length > 0 ) {
- var processedPost = querystring.parse( persistenceData );
- fs.writeFileSync( helpers.JoinPath( './documents', parsedRequest[ 'public_path' ], saveName, 'saveState' + processedPost[ "extension" ] ), processedPost[ "jsonState" ] );
- fs.writeFileSync( helpers.JoinPath( './documents', parsedRequest[ 'public_path' ], saveName, 'saveState_' + saveRevision + processedPost[ "extension" ] ), processedPost[ "jsonState" ] );
- }
- } );
- return true;
- }
- return false;
- }
- // The Serve function takes the nodeJS request, nodeJS response and the parsedRequest, and
- // attempts to see if it is a properly formed persistence related request.
- // If so, it serves the request, and returns true.
- // If it is an incorrectly formed persistence request, or not a persistence request
- // at all, then false will be returned.
- function Serve( request, response, parsedRequest ) {
- if ( parsedRequest[ 'private_path' ] ) {
- var segments = helpers.GenerateSegments( parsedRequest[ 'private_path' ] );
- if ( segments.length > 0 ) {
- switch ( segments[ 0 ] ) {
- case "listallsaves":
- if ( request.method == "GET" ) {
- var saveInfo = GetSaveInformationRecursive( request, response, "/" );
- serve.JSON( saveInfo, response, url.parse( request.url, true ) );
- return true;
- }
- return false;
- case "listsaves":
- if ( request.method == "GET" ) {
- var saveInfo = GetSaveInformation( request, response, parsedRequest[ 'public_path' ] );
- serve.JSON( saveInfo, response, url.parse( request.url, true ) );
- return true;
- }
- return false;
- case "listdescendentsaves":
- if ( request.method == "GET" ) {
- var saveInfo = GetSaveInformationRecursive( request, response, parsedRequest[ 'public_path' ] );
- serve.JSON( saveInfo, response, url.parse( request.url, true ) );
- return true;
- }
- return false;
- case "load":
- return HandlePersistenceLoad( request, response, parsedRequest, segments );
- case "saves":
- return HandleSavesLoad( request, response, parsedRequest, segments );
- case "save":
- if ( ( request.method == "POST" ) && ( segments.length > 1 ) ) {
- return HandlePersistenceSave( request, response, parsedRequest, segments );
- }
- }
- }
- }
- return false;
- }
- exports.GetLoadInformation = GetLoadInformation;
- exports.Serve = Serve;
- exports.LoadSaveObject = LoadSaveObject;
|