From f45b7ce9ead53215eb8f63ab744361ae3f6057f2 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Wed, 3 Aug 2011 19:31:25 +0100 Subject: [PATCH] Installed API infrastructure, getText works already --- .gitignore | 3 +- node/db/API.js | 457 +++++++++++++++++++++++++++++++++++++ node/db/GroupManager.js | 21 ++ node/db/PadManager.js | 12 +- node/db/SessionManager.js | 19 ++ node/handler/APIHandler.js | 139 +++++++++++ node/server.js | 26 ++- 7 files changed, 672 insertions(+), 5 deletions(-) create mode 100644 node/db/API.js create mode 100644 node/db/GroupManager.js create mode 100644 node/db/SessionManager.js create mode 100644 node/handler/APIHandler.js diff --git a/.gitignore b/.gitignore index df6d75bec..25c577ecc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules settings.json -static/js/jquery.min.js \ No newline at end of file +static/js/jquery.min.js +APIKEY.txt diff --git a/node/db/API.js b/node/db/API.js new file mode 100644 index 000000000..d53e50121 --- /dev/null +++ b/node/db/API.js @@ -0,0 +1,457 @@ +/** + * This module provides all API functions + */ + +/* + * 2011 Peter 'Pita' Martischka + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var padManager = require("./PadManager"); +var async = require("async"); + +/**********************/ +/**GROUP FUNCTIONS*****/ +/**********************/ + +/** +createGroup() creates a new group + +Example returns: + +{code: 0, message:"ok", data: {groupID: 5}} +*/ +exports.createGroup = function (callback) +{ + +} + +/** +getMappedGroup4(groupMapper) this functions helps you to map your application group ids to etherpad lite group ids + +Example returns: + +{code: 0, message:"ok", data: {groupID: 7}} +*/ +exports.getMappedGroup4 = function (groupMapper, callback) +{ + +} + +/** +deleteGroup(groupID) deletes a group + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"There is no group for this groupID", data: null} +*/ +exports.deleteGroup = function(groupID, callback) +{ + +} + +/** +listPads(groupID) returns all pads of this group + +Example returns: + +{code: 0, message:"ok", data: {padIDs : ["3$test", "3$test2"]} +{code: 1, message:"There is no group for this groupID", data: null} +*/ +exports.listPads = function(groupID, callback) +{ + +} + +/** +createPad(groupID, padName [, text]) creates a new pad in this group + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"pad does already exist", data: null} +{code: 1, message:"There is no group for this groupID", data: null} +*/ +exports.createPad = function(groupID, padName, text, callback) +{ + +} + +/**********************/ +/**AUTHOR FUNCTIONS****/ +/**********************/ + + +/** +createAuthor([name]) creates a new author + +Example returns: + +{code: 0, message:"ok", data: {authorID: 5}} +*/ +exports.createAuthor = function(name, callback) +{ + +} + +/** +getMappedAuthor4(authorMapper [, name]) this functions helps you to map your application author ids to etherpad lite author ids + +Example returns: + +{code: 0, message:"ok", data: {authorID: 5}} +*/ +exports.getMappedAuthor4 = function(authorMapper ,name, callback) +{ + +} + +/**********************/ +/**SESSION FUNCTIONS***/ +/**********************/ + +/** +createSession(groupID, authorID, validUntil) creates a new session + +Example returns: + +{code: 0, message:"ok", data: {sessionID: 5}} +{code: 1, message:"groupID doesn't exist", data: null} +{code: 1, message:"authorID doesn't exist", data: null} +{code: 1, message:"validUntil is in the past", data: null} +*/ +exports.createSession = function(groupID, authorID, validUntil, callback) +{ + +} + +/** +deleteSession(sessionID) deletes a session + +Example returns: + +{code: 1, message:"ok", data: null} +{code: 1, message:"sessionID does not exist", data: null} +*/ +exports.deleteSession = function(sessionID, callback) +{ + +} + +/** +getSessionInfo(sessionID) returns informations about a session + +Example returns: + +{code: 0, message:"ok", data: {authorID: 5, groupID: 7, validUntil: 1312201246}} +{code: 1, message:"sessionID does not exist", data: null} +*/ +exports.getSessionInfo = function(sessionID, callback) +{ + +} + +/** +listSessionsOfGroup(groupID) returns all sessions of a group + +Example returns: + +{code: 0, message:"ok", data: {32: {authorID: 5, groupID: 7, validUntil: 1312201246}, 53: {authorID: 3, groupID: 2, validUntil: 1312201216}}} +{code: 1, message:"groupID does not exist", data: null} +*/ +exports.listSessionsOfGroup = function(groupID, callback) +{ + +} + +/** +listSessionsOfAuthor(authorID) returns all sessions of an author + +Example returns: + +{code: 0, message:"ok", data: {32: {authorID: 5, groupID: 7, validUntil: 1312201246}, 53: {authorID: 3, groupID: 2, validUntil: 1312201216}}} +{code: 1, message:"authorID does not exist", data: null} +*/ +exports.listSessionsOfAuthor = function(authorID, callback) +{ + +} + +/** +deleteAllSessionsOfGroup(groupID) deletes all sessions of a group + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"groupID does not exist", data: null} +*/ +exports.deleteAllSessionsOfGroup = function(groupID, callback) +{ + +} + +/** +deleteAllSessionsOfAuthor(authorID) deletes all sessions of an author + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"authorID does not exist", data: null} +*/ +exports.deleteAllSessionsOfAuthor = function(authorID, callback) +{ + +} + +/************************/ +/**PAD CONTENT FUNCTIONS*/ +/************************/ + +/** +getText(padID, [rev]) returns the text of a pad + +Example returns: + +{code: 0, message:"ok", data: {text:"Welcome Text"}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getText = function(padID, rev, callback) +{ + //check if rev is set + if(typeof rev == "function") + { + callback = rev; + rev = undefined; + } + + //check if padID is a string + if(typeof padID != "string") + { + callback({stop: "padID is not a string"}); + return; + } + + //check if rev is a number + if(rev !== undefined && typeof rev != "number") + { + //try to parse the number + if(!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback({stop: "rev is not a number"}); + return; + } + } + + //ensure this is not a negativ number + if(rev !== undefined && rev < 0) + { + callback({stop: "rev is a negativ number"}); + return; + } + + //ensure this is not a float value + if(rev !== undefined && !is_int(rev)) + { + callback({stop: "rev is a float value"}); + return; + } + + var pad; + var data; + + async.series([ + //check if pad exists + function(callback) + { + padManager.doesPadExists(padID, function(err, exists) + { + if(err) + { + callback(err); + } + else + { + callback(exists == false ? {stop: "padID does not exist"} : null) + } + }); + }, + //get the pad object + function(callback) + { + padManager.getPad(padID, function(err, _pad) + { + pad=_pad; + callback(err); + }); + }, + //return the text + function(callback) + { + //the client asked for a special revision + if(rev !== undefined) + { + //check if this is a valid revision + if(rev > pad.getHeadRevisionNumber()) + { + callback({stop: "rev is higher than the head revision of the pad"}); + return; + } + + //get the text of this revision + pad.getInternalRevisionAText(rev, function(err, atext) + { + if(!err) + { + data = {text: atext.text}; + } + + callback(err); + }) + } + //the client wants the latest text, lets return it to him + else + { + data = {"text": pad.text()}; + callback(); + } + } + ], function(err) + { + callback(err, data) + }); +} + +/** +setText(padID, text) sets the text of a pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +{code: 1, message:"text too long", data: null} +*/ +exports.setText = function(padID, text, callback) +{ + +} + +/*****************/ +/**PAD FUNCTIONS */ +/*****************/ + +/** +getRevisionsCount(padID) returns the number of revisions of this pad + +Example returns: + +{code: 0, message:"ok", data: {revisions: 56}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getRevisionsCount = function(padID, callback) +{ + +} + +/** +deletePad(padID) deletes a pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.deletePad = function(padID, callback) +{ + +} + +/** +getReadOnlyLink(padID) returns the read only link of a pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getReadOnlyLink = function(padID, callback) +{ + +} + +/** +setPublicStatus(padID, publicStatus) sets a boolean for the public status of a pad + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.setPublicStatus = function(padID, publicStatus, callback) +{ + +} + +/** +getPublicStatus(padID) return true of false + +Example returns: + +{code: 0, message:"ok", data: {publicStatus: true}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.getPublicStatus = function(padID, callback) +{ + +} + +/** +setPassword(padID, password) returns ok or a error message + +Example returns: + +{code: 0, message:"ok", data: null} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.setPassword = function(padID, password, callback) +{ + +} + +/** +isPasswordProtected(padID) returns true or false + +Example returns: + +{code: 0, message:"ok", data: {passwordProtection: true}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.isPasswordProtected = function(padID, callback) +{ + +} + +/******************************/ +/** INTERNAL HELPER FUNCTIONS */ +/******************************/ + +//checks if a number is an int +function is_int(value) +{ + return (parseFloat(value) == parseInt(value)) && !isNaN(value) +} diff --git a/node/db/GroupManager.js b/node/db/GroupManager.js new file mode 100644 index 000000000..2ceb1b656 --- /dev/null +++ b/node/db/GroupManager.js @@ -0,0 +1,21 @@ +/** + * The Group Manager provides functions to manage groups in the database + */ + +/* + * 2011 Peter 'Pita' Martischka + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + diff --git a/node/db/PadManager.js b/node/db/PadManager.js index 04ab7eb7f..4fada2c50 100644 --- a/node/db/PadManager.js +++ b/node/db/PadManager.js @@ -19,6 +19,7 @@ */ require("../db/Pad"); +var db = require("./DB").db; /** * A Array with all known Pads @@ -58,6 +59,13 @@ exports.getPad = function(id, callback) } }); } - - //globalPads[id].timestamp = new Date().getTime(); +} + +//checks if a pad exists +exports.doesPadExists = function(padId, callback) +{ + db.get("pad:"+padId, function(err, value) + { + callback(err, value != null); + }); } diff --git a/node/db/SessionManager.js b/node/db/SessionManager.js new file mode 100644 index 000000000..f25389111 --- /dev/null +++ b/node/db/SessionManager.js @@ -0,0 +1,19 @@ +/** + * The Session Manager provides functions to manage session in the database + */ + +/* + * 2011 Peter 'Pita' Martischka + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js new file mode 100644 index 000000000..2520bda3e --- /dev/null +++ b/node/handler/APIHandler.js @@ -0,0 +1,139 @@ +/** + * The API Handler handles all API http requests + */ + +/* + * 2011 Peter 'Pita' Martischka + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var fs = require("fs"); +var api = require("../db/API"); + +//ensure we have an apikey +var apikey = null; +try +{ + apikey = fs.readFileSync("../APIKEY.txt","utf8"); +} +catch(e) +{ + apikey = randomString(32); + fs.writeFileSync("../APIKEY.txt",apikey,"utf8"); +} + +//a list of all functions +var functions = { +// "createGroup" : [], +// "getMappedGroup4" : ["groupMapper"], +// "deleteGroup" : ["groupID"], +// "listPads" : ["groupID"], +// "createPad" : ["groupID", "padName", "text"], +// "createAuthor" : ["name"], +// "getMappedAuthor4" : ["authorMapper" , "name"], +// "createSession" : ["groupID", "authorID", "validUntil"], +// "deleteSession" : ["sessionID"], +// "getSessionInfo" : ["sessionID"], +// "listSessionsOfGroup" : ["groupID"], +// "listSessionsOfAuthor" : ["authorID"], +// "deleteAllSessionsOfGroup" : ["groupID"], +// "deleteAllSessionsOfAuthor" : ["authorID"], + "getText" : ["padID", "rev"] +// "setText" : ["padID", "text"] +// "getRevisionsCount" : ["padID"], +// "deletePad" : ["padID"], +// "getReadOnlyLink" : ["padID"], +// "setPublicStatus" : ["padID", "publicStatus"], +// "getPublicStatus" : ["padID"], +// "setPassword" : ["padID", "password"], +// "isPasswordProtected" : ["padID"] +}; + +/** + * Handles a HTTP API call + * @param functionName the name of the called function + * @param fields the params of the called function + * @req express request object + * @res express response object + */ +exports.handle = function(functionName, fields, req, res) +{ + //check the api key! + if(fields["apikey"] != apikey) + { + res.send({code: 4, message: "no or wrong API Key", data: null}); + return; + } + + //check if this is a valid function name + var isKnownFunctionname = false; + for(var knownFunctionname in functions) + { + if(knownFunctionname == functionName) + { + isKnownFunctionname = true; + break; + } + } + + //say goodbye if this is a unkown function + if(!isKnownFunctionname) + { + res.send({code: 3, message: "no such function", data: null}); + return; + } + + //put the function parameters in an array + var functionParams = []; + for(var i=0;i