mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-31 07:32:59 +00:00 
			
		
		
		
	Marginally improve the elegance of the simple HTTP server
This commit is contained in:
		| @@ -12,151 +12,223 @@ Serve tiddlers over http | ||||
| /*global $tw: false */ | ||||
| "use strict"; | ||||
|  | ||||
| if(!$tw.browser) { | ||||
| 	var util = require("util"), | ||||
| 		fs = require("fs"), | ||||
| 		url = require("url"), | ||||
| 		path = require("path"), | ||||
| 		http = require("http");	 | ||||
| } | ||||
|  | ||||
| exports.info = { | ||||
| 	name: "server", | ||||
| 	synchronous: true | ||||
| }; | ||||
|  | ||||
| var Command = function(params,commander,callback) { | ||||
| 	this.params = params; | ||||
| 	this.commander = commander; | ||||
| 	this.callback = callback; | ||||
| /* | ||||
| A simple HTTP server with regexp-based routes | ||||
| */ | ||||
| function SimpleServer(options) { | ||||
| 	this.routes = options.routes || []; | ||||
| 	this.wiki = options.wiki; | ||||
| 	this.variables = options.variables || {}; | ||||
| } | ||||
|  | ||||
| SimpleServer.prototype.set = function(obj) { | ||||
| 	var self = this; | ||||
| 	$tw.utils.each(obj,function(value,name) { | ||||
| 		self.variables[name] = value; | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| Command.prototype.execute = function() { | ||||
| 	var self = this, | ||||
| 		util = require("util"), | ||||
| 		fs = require("fs"), | ||||
| 		url = require("url"), | ||||
| 		path = require("path"), | ||||
| 		http = require("http"), | ||||
| 		port = this.params[0] || "8080", | ||||
| 		rootTiddler = this.params[1] || "$:/core/templates/tiddlywiki5.template.html", | ||||
| 		renderType = this.params[2] || "text/plain", | ||||
| 		serveType = this.params[3] || "text/html", | ||||
| 		prefix; | ||||
| SimpleServer.prototype.get = function(name) { | ||||
| 	return this.variables[name]; | ||||
| }; | ||||
|  | ||||
| SimpleServer.prototype.addRoute = function(route) { | ||||
| 	this.routes.push(route); | ||||
| }; | ||||
|  | ||||
| SimpleServer.prototype.listen = function(port) { | ||||
| 	var self = this; | ||||
| 	http.createServer(function(request, response) { | ||||
| 		var requestPath = url.parse(request.url).pathname, | ||||
| 			text; | ||||
| 		// Compose the state object | ||||
| 		var state = {}; | ||||
| 		state.wiki = self.wiki; | ||||
| 		state.server = self; | ||||
| 		state.urlInfo = url.parse(request.url); | ||||
| 		// Find the route that matches this path | ||||
| 		var route; | ||||
| 		for(var t=0; t<self.routes.length; t++) { | ||||
| 			var potentialRoute = self.routes[t], | ||||
| 				pathRegExp = potentialRoute.path, | ||||
| 				match = potentialRoute.path.exec(state.urlInfo.pathname); | ||||
| 			if(request.method === potentialRoute.method && match) { | ||||
| 				state.params = []; | ||||
| 				for(var p=1; p<match.length; p++) { | ||||
| 					state.params.push(match[p]); | ||||
| 				} | ||||
| 				route = potentialRoute; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		// Return a 404 if we didn't find a route | ||||
| 		if(!route) { | ||||
| 			response.writeHead(404); | ||||
| 			response.end(); | ||||
| 			return; | ||||
| 		} | ||||
| 		// Dispatch the appropriate method | ||||
| 		switch(request.method) { | ||||
| 			case "GET": // Intentional fall-through | ||||
| 			case "DELETE": | ||||
| 				route.handler(request,response,state); | ||||
| 				break; | ||||
| 			case "PUT": | ||||
| 				var data = ""; | ||||
| 				request.on("data",function(chunk) { | ||||
| 					data += chunk.toString(); | ||||
| 				}); | ||||
| 				request.on("end",function() { | ||||
| 					prefix = "/recipes/default/tiddlers/"; | ||||
| 					if(requestPath.indexOf(prefix) === 0) { | ||||
| 						var title = decodeURIComponent(requestPath.substr(prefix.length)), | ||||
| 							fields = JSON.parse(data); | ||||
| 						// Use the title from the PUT URL if we don't have one | ||||
| 						if(!fields.title) { | ||||
| 							fields.title = title; | ||||
| 						} | ||||
| 						// Pull up any subfields in the `fields` object | ||||
| 						if(fields.fields) { | ||||
| 							$tw.utils.each(fields.fields,function(field,name) { | ||||
| 								fields[name] = field; | ||||
| 							}); | ||||
| 							delete fields.fields; | ||||
| 						} | ||||
| 						// Remove any revision field | ||||
| 						if(fields["revision"]) { | ||||
| 							delete fields["revision"]; | ||||
| 						} | ||||
| console.log("PUT tiddler",title,fields) | ||||
| 						self.commander.wiki.addTiddler(new $tw.Tiddler(JSON.parse(data),{title: title})); | ||||
| 						var changeCount = self.commander.wiki.getChangeCount(title).toString(); | ||||
| 						response.writeHead(204, "OK",{ | ||||
| 							Etag: "\"default/" + title + "/" + changeCount + ":\"" | ||||
| 						}); | ||||
| 						response.end(); | ||||
| 					} else { | ||||
| 						response.writeHead(404); | ||||
| 						response.end(); | ||||
| 					} | ||||
| 					state.data = data; | ||||
| 					route.handler(request,response,state); | ||||
| 				}); | ||||
| 				break; | ||||
| 			case "DELETE": | ||||
| 				prefix = "/bags/default/tiddlers/"; | ||||
| 				if(requestPath.indexOf(prefix) === 0) { | ||||
| 					var title = decodeURIComponent(requestPath.substr(prefix.length)); | ||||
| 		} | ||||
| 	}).listen(port); | ||||
| }; | ||||
|  | ||||
| var Command = function(params,commander,callback) { | ||||
| 	this.params = params; | ||||
| 	this.commander = commander; | ||||
| 	this.callback = callback; | ||||
| 	// Set up server | ||||
| 	this.server = new SimpleServer({ | ||||
| 		wiki: this.commander.wiki | ||||
| 	}); | ||||
| 	// Add route handlers | ||||
| 	this.server.addRoute({ | ||||
| 		method: "PUT", | ||||
| 		path: /^\/recipes\/default\/tiddlers\/(.+)$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			var title = decodeURIComponent(state.params[0]), | ||||
| 				fields = JSON.parse(state.data); | ||||
| 			// Pull up any subfields in the `fields` object | ||||
| 			if(fields.fields) { | ||||
| 				$tw.utils.each(fields.fields,function(field,name) { | ||||
| 					fields[name] = field; | ||||
| 				}); | ||||
| 				delete fields.fields; | ||||
| 			} | ||||
| 			// Remove any revision field | ||||
| 			if(fields["revision"]) { | ||||
| 				delete fields["revision"]; | ||||
| 			} | ||||
| console.log("PUT tiddler",title,fields) | ||||
| 			state.wiki.addTiddler(new $tw.Tiddler(fields,{title: title})); | ||||
| 			var changeCount = state.wiki.getChangeCount(title).toString(); | ||||
| 			response.writeHead(204, "OK",{ | ||||
| 				Etag: "\"default/" + title + "/" + changeCount + ":\"" | ||||
| 			}); | ||||
| 			response.end(); | ||||
| 		} | ||||
| 	}); | ||||
| 	this.server.addRoute({ | ||||
| 		method: "DELETE", | ||||
| 		path: /^\/bags\/default\/tiddlers\/(.+)$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			var title = decodeURIComponent(state.params[0]); | ||||
| console.log("DELETE tiddler",title) | ||||
| 					self.commander.wiki.deleteTiddler(decodeURIComponent(title)); | ||||
| 					response.writeHead(204, "OK"); | ||||
| 					response.end(); | ||||
| 				} else { | ||||
| 					response.writeHead(404); | ||||
| 					response.end(); | ||||
| 				} | ||||
| 				break; | ||||
| 			case "GET": | ||||
| 				if(requestPath === "/") { | ||||
| 					response.writeHead(200, {"Content-Type": serveType}); | ||||
| 					text = self.commander.wiki.renderTiddler(renderType,rootTiddler); | ||||
| 					response.end(text,"utf8"); | ||||
| 				} else if(requestPath === "/status") { | ||||
| 					response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 					text = JSON.stringify({ | ||||
| 						username: "ANONYMOUS", | ||||
| 						space: { | ||||
| 							recipe: "default" | ||||
| 						}, | ||||
| 						tiddlywiki_version: $tw.version | ||||
| 					}); | ||||
| 					response.end(text,"utf8"); | ||||
| 				} else if(requestPath === "/recipes/default/tiddlers.json") { | ||||
| 					response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 					var tiddlers = []; | ||||
| 					$tw.wiki.forEachTiddler("title",function(title,tiddler) { | ||||
| 						var tiddlerFields = {}; | ||||
| 						$tw.utils.each(tiddler.fields,function(field,name) { | ||||
| 							if(name !== "text") { | ||||
| 								tiddlerFields[name] = tiddler.getFieldString(name); | ||||
| 							} | ||||
| 						}); | ||||
| 						tiddlerFields["revision"] = $tw.wiki.getChangeCount(title); | ||||
| 						tiddlers.push(tiddlerFields); | ||||
| 					}); | ||||
| 					text = JSON.stringify(tiddlers); | ||||
| 					response.end(text,"utf8"); | ||||
| 				} else { | ||||
| 					prefix = "/recipes/default/tiddlers/"; | ||||
| 					if(requestPath.indexOf(prefix) === 0) { | ||||
| 						var title = decodeURIComponent(requestPath.substr(prefix.length)), | ||||
| 							tiddler = $tw.wiki.getTiddler(title), | ||||
| 							tiddlerFields = {}, | ||||
| 							knownFields = [ | ||||
| 								"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri" | ||||
| 							]; | ||||
| 						if(tiddler) { | ||||
| 							$tw.utils.each(tiddler.fields,function(field,name) { | ||||
| 								var value = tiddler.getFieldString(name) | ||||
| 								if(knownFields.indexOf(name) !== -1) { | ||||
| 									tiddlerFields[name] = value; | ||||
| 								} else { | ||||
| 									tiddlerFields.fields = tiddlerFields.fields || {}; | ||||
| 									tiddlerFields.fields[name] = value; | ||||
| 								} | ||||
| 							}); | ||||
| 							response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 							response.end(JSON.stringify(tiddlerFields),"utf8"); | ||||
| 						} else { | ||||
| 							response.writeHead(404); | ||||
| 							response.end(); | ||||
| 						} | ||||
| 					} else { | ||||
| 						response.writeHead(404); | ||||
| 						response.end(); | ||||
| 			state.wiki.deleteTiddler(title); | ||||
| 			response.writeHead(204, "OK"); | ||||
| 			response.end(); | ||||
| 		} | ||||
| 	}); | ||||
| 	this.server.addRoute({ | ||||
| 		method: "GET", | ||||
| 		path: /^\/$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			response.writeHead(200, {"Content-Type": state.server.get("serveType")}); | ||||
| 			var text = state.wiki.renderTiddler(state.server.get("renderType"),state.server.get("rootTiddler")); | ||||
| 			response.end(text,"utf8"); | ||||
| 		} | ||||
| 	}); | ||||
| 	this.server.addRoute({ | ||||
| 		method: "GET", | ||||
| 		path: /^\/status$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 			var text = JSON.stringify({ | ||||
| 				username: "ANONYMOUS", | ||||
| 				space: { | ||||
| 					recipe: "default" | ||||
| 				}, | ||||
| 				tiddlywiki_version: $tw.version | ||||
| 			}); | ||||
| 			response.end(text,"utf8"); | ||||
| 		} | ||||
| 	}); | ||||
| 	this.server.addRoute({ | ||||
| 		method: "GET", | ||||
| 		path: /^\/recipes\/default\/tiddlers.json$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 			var tiddlers = []; | ||||
| 			state.wiki.forEachTiddler("title",function(title,tiddler) { | ||||
| 				var tiddlerFields = {}; | ||||
| 				$tw.utils.each(tiddler.fields,function(field,name) { | ||||
| 					if(name !== "text") { | ||||
| 						tiddlerFields[name] = tiddler.getFieldString(name); | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 			case "POST": | ||||
| 				}); | ||||
| 				tiddlerFields["revision"] = $tw.wiki.getChangeCount(title); | ||||
| 				tiddlers.push(tiddlerFields); | ||||
| 			}); | ||||
| 			var text = JSON.stringify(tiddlers); | ||||
| 			response.end(text,"utf8"); | ||||
| 		} | ||||
| 	}); | ||||
| 	this.server.addRoute({ | ||||
| 		method: "GET", | ||||
| 		path: /^\/recipes\/default\/tiddlers\/(.+)$/, | ||||
| 		handler: function(request,response,state) { | ||||
| 			var title = decodeURIComponent(state.params[0]), | ||||
| 				tiddler = state.wiki.getTiddler(title), | ||||
| 				tiddlerFields = {}, | ||||
| 				knownFields = [ | ||||
| 					"bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri" | ||||
| 				]; | ||||
| 			if(tiddler) { | ||||
| 				$tw.utils.each(tiddler.fields,function(field,name) { | ||||
| 					var value = tiddler.getFieldString(name) | ||||
| 					if(knownFields.indexOf(name) !== -1) { | ||||
| 						tiddlerFields[name] = value; | ||||
| 					} else { | ||||
| 						tiddlerFields.fields = tiddlerFields.fields || {}; | ||||
| 						tiddlerFields.fields[name] = value; | ||||
| 					} | ||||
| 				}); | ||||
| 				response.writeHead(200, {"Content-Type": "application/json"}); | ||||
| 				response.end(JSON.stringify(tiddlerFields),"utf8"); | ||||
| 			} else { | ||||
| 				response.writeHead(404); | ||||
| 				response.end(); | ||||
| 				break; | ||||
| 			} | ||||
| 	}).listen(port); | ||||
| 		} | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| Command.prototype.execute = function() { | ||||
| 	var port = this.params[0] || "8080", | ||||
| 		rootTiddler = this.params[1] || "$:/core/templates/tiddlywiki5.template.html", | ||||
| 		renderType = this.params[2] || "text/plain", | ||||
| 		serveType = this.params[3] || "text/html"; | ||||
| 	this.server.set({ | ||||
| 		rootTiddler: rootTiddler, | ||||
| 		renderType: renderType, | ||||
| 		serveType: serveType | ||||
| 	}); | ||||
| 	this.server.listen(port); | ||||
| 	if(this.commander.verbose) { | ||||
| 		console.log("Serving on port " + port); | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jeremy Ruston
					Jeremy Ruston