├── .gitignore
├── templates
├── new.html
└── list.html
├── static
└── styles.css
├── .gitmodules
├── index.html
├── README
├── blog.js
└── b.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | posts/*
3 |
--------------------------------------------------------------------------------
/templates/new.html:
--------------------------------------------------------------------------------
1 |
2 | <%= title %>
3 | <%= content %>
4 |
5 |
--------------------------------------------------------------------------------
/static/styles.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-family:Georgia;
3 | font-size:62px;
4 | }
5 | form{
6 |
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "underscore"]
2 | path = underscore
3 | url = git://github.com/furtivefelon/underscore
4 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/templates/list.html:
--------------------------------------------------------------------------------
1 |
2 | Hello everyone! Welcome to the start of blog.js, a blogging engine based on node.js . The code is currently resides at github, and is released under MIT/BSD license. It is my hope that this blogging framework will be a good example of how to build a full-fledged web application using node js (and a custom framework that anyone can use tentatively named b.js.
3 | On this page, you can play around with the current version of blog.js, and if you have any questions, please direct it to the github page. I (jasonwang), also hang out in #node.js at irc.freenode.net
4 | Add new Entry
5 |
6 | <% _.each(titles, function(title){ %>
7 | <%= title %>
8 | <% }); %>
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Blog.js
2 |
3 | Released under a standard MIT/BSD license (take your pick).
4 |
5 | This document talks about how to use the framework evolved through the development of this blogging engine. There are two goals for this project. The first of which is to develop a blogging engine mature enough for my personal use, and perhaps is good enough for many others as well. The second goal is to provide a standard demonstration of how to develop a full-fledged web application using node.js.
6 |
7 | To use the framework, please include the following line in your application: require(./b);
8 |
9 | Type the following incantation to pull the source to your folder:
10 |
11 | git clone git://github.com/furtivefelon/blog.js
12 | cd blog.js
13 | git submodule init
14 | git submodule update
15 |
16 | Also, open blog.js, and edit BLOG_PREFIX to the full path to the blog.js folder. After, cd into the blog.js folder, and type mkdir posts. Then assuming node.js is install properly, you can then type node blog.js to run the server. Go to http://127.0.0.1:7000/addForm to add an entry, then go to http://127.0.0.1:7000 to view the result!
17 |
18 |
--------------------------------------------------------------------------------
/blog.js:
--------------------------------------------------------------------------------
1 | PORT = 7000
2 | HOST = null;
3 | BLOG_PREFIX = '/Users/aaron/Projects/git/forks/blog.js';
4 | POSTS = '/posts';
5 | TEMPLATE = '/templates';
6 |
7 | var b = require('./b');
8 | require('./underscore/underscore');
9 | var sys = require('sys');
10 | var fs = require('fs');
11 |
12 | postdir = BLOG_PREFIX+POSTS+'/';
13 | templatedir = BLOG_PREFIX+TEMPLATE+'/';
14 |
15 | b.listen(PORT, HOST);
16 |
17 | function template(temp, data){
18 | var t = fs.readFileSync(templatedir+temp, 'utf8');
19 | var output = _.template(t, data);
20 | return output;
21 | }
22 |
23 | b.get('^/$', function(req, res){
24 | fs.readdir(postdir,function(err,files){
25 | titles = _.map(files, function(file){
26 | var post = postdir+file;
27 | var ret;
28 | var stats = fs.statSync(postdir+file);
29 | if(stats.isFile()){
30 | var data = fs.readFileSync(postdir+file, 'utf8');
31 | ret = JSON.parse(data)['title'];
32 | } else {
33 | sys.puts(post + ' is not a valid file!');
34 | ret = 0;
35 | }
36 | return ret;
37 | });
38 | titles = {'titles':titles};
39 | var html = template('list.html', titles);
40 | res.sendHTML(html);
41 | });
42 | });
43 |
44 | b.get('^/addForm$', b.sendFile('index.html'));
45 | b.post('/new', function(req, res){
46 | var data = JSON.stringify(req.params);
47 | var file = postdir + req.params['title'];
48 | var promise = fs.open(file, process.O_WRONLY|process.O_CREAT|process.O_TRUNC, 0666,function(err,fd){
49 | fs.write(fd, data, null, 'UTF8',function(err,written){
50 | if(err){
51 | sys.puts('Error loading '+file);
52 | }
53 | sys.puts(written+' bytes are written');
54 | var html = template('new.html', req.params);
55 | res.sendHTML(html);
56 | });
57 | });
58 | });
59 |
60 |
61 | b.get('^/view/([a-zA-Z0-9]+)$', function(req, res, title){
62 | file = postdir+title;
63 | data = fs.readFileSync(file, 'utf8');
64 | data = JSON.parse(data);
65 | var html = template('new.html', data);
66 | res.sendHTML(html);
67 | });
68 |
69 | b.setStaticPath(BLOG_PREFIX, '/static');
70 |
71 |
--------------------------------------------------------------------------------
/b.js:
--------------------------------------------------------------------------------
1 | var CS = require('http').createServer;
2 | var fs = require('fs');
3 | var sys = require('sys');
4 | require('./underscore/underscore');
5 |
6 | var b = exports;
7 |
8 |
9 |
10 | getMap = {};
11 | postMap = {};
12 |
13 | b.setStaticPath = function(prefix, path){
14 | fs.stat(prefix + path,function(err,stats){
15 | if(stats.isDirectory()){
16 | fs.readdir(prefix+path,function(err,files){
17 | _.each(files, function(file){
18 | b.setStaticPath(prefix, path+'/'+file);
19 | });
20 | });
21 | } else if(stats.isFile()){
22 | getMap[path] = b.sendFile(prefix+path);
23 | } else {
24 | sys.puts('ERROR: Path: '+prefix+path+' is not a valid file or directory!');
25 | }
26 | });
27 | };
28 |
29 | b.get = function(path, handler){
30 | getMap[path] = handler;
31 | };
32 |
33 | b.post = function(path, handler){
34 | postMap[path] = handler;
35 | };
36 |
37 | function notFound(req, res){
38 | var NOT_FOUND = 'Not Found\n';
39 | sys.puts('404 ERROR');
40 | res.sendHTML(NOT_FOUND, 404);
41 | }
42 |
43 | var server = CS(function(req, res){
44 | res.sendHTML = function(body, code){
45 | code = code || 200;
46 | header = [['Content-Type', 'text/html; charset=utf-8']
47 | ,['Content-Length', body.length]];
48 | this.writeHead(code, header);
49 | this.write(unescape(body));
50 | this.end();
51 | };
52 | // Currently, if the request is GET, then only header is mostly
53 | // relevant. If the request is POST, then the body may be relevant
54 | // However, in most cases, you only need to scrap the form submision
55 | // things, therefore, it will be availiable in param as needed,
56 | // However, body of the request will be availible in req.body
57 | if(req.method === 'GET'){
58 | var handled = false;
59 | if(!handler){
60 | _.each(_.keys(getMap), function(key){
61 | var args = new RegExp(key).exec(req.url);
62 | if(args){
63 | args.shift();
64 | args.unshift(req, res);
65 | handled = true;
66 | getMap[key].apply(this, args);
67 | _.breakLoop();
68 | }
69 | });
70 | }
71 | if(!handled) notFound(req, res);
72 | } else if(req.method === 'POST'){
73 | var handler = postMap[req.url] || notFound;
74 | req.body = '';
75 | req.addListener('data', function(chunk){sys.puts("chunk = " + chunk);req.body = chunk;});
76 | req.addListener('end', function(){
77 | var info = /([^=&]+)=([^&]+)/ig;
78 | var match;
79 |
80 | req.params = req.params || {};
81 | while((match = info.exec(req.body)) != null){
82 | sys.puts("puts: post vars = " + match[1] + " = " + match[2] )
83 | req.params[match[1]] = match[2];
84 | }
85 | handler(req, res);
86 | });
87 | }
88 | });
89 |
90 | b.getPostParams = function(req, callback){
91 | var body = '';
92 | req.addListener('body', function(chunk){body += chunk;})
93 | .addListener('complete', function(){
94 | callback(unescape(body.substring(8).replace(/\+/g,' ')));
95 | });
96 | }
97 |
98 | b.listen = function(port, host){
99 | server.listen(port, host);
100 | sys.puts('Server at http://' + (host || '127.0.0.1') + ':' + port.toString() + '/');
101 | }
102 |
103 | function extname(path){
104 | var index = path.lastIndexOf('.');
105 | return index < 0 ? '' : path.substring(index);
106 | }
107 |
108 | b.sendFile = function(filename){
109 | var body, headers;
110 | var content_type = b.mime.lookupExtension(extname(filename));
111 | function loadResponseData(callback){
112 | if(body && headers){
113 | callback();
114 | return;
115 | }
116 | sys.puts("Loading " + filename + '...');
117 | var file = fs.readFile(filename, 'utf8',function(err,data){
118 | if(err){
119 | sys.puts('Error ' + err + ' while loading ' + filename);
120 | }
121 | body = data;
122 | headers = [['Content-Type', content_type]
123 | ,['Content-Length', data.length]];
124 | sys.puts('static file' + filename + ' loaded');
125 | callback();
126 | });
127 | }
128 | return function(req, res){
129 | loadResponseData(function(){
130 | res.writeHead(200, headers);
131 | res.write(body);
132 | res.end();
133 | });
134 | }
135 | };
136 |
137 | // stolen from jack- thanks
138 | b.mime = {
139 | // returns MIME type for extension, or fallback, or octet-steam
140 | lookupExtension : function(ext, fallback) {
141 | return b.mime.TYPES[ext.toLowerCase()] || fallback || 'application/octet-stream';
142 | },
143 |
144 | // List of most common mime-types, stolen from Rack.
145 | TYPES : { ".3gp" : "video/3gpp"
146 | , ".a" : "application/octet-stream"
147 | , ".ai" : "application/postscript"
148 | , ".aif" : "audio/x-aiff"
149 | , ".aiff" : "audio/x-aiff"
150 | , ".asc" : "application/pgp-signature"
151 | , ".asf" : "video/x-ms-asf"
152 | , ".asm" : "text/x-asm"
153 | , ".asx" : "video/x-ms-asf"
154 | , ".atom" : "application/atom+xml"
155 | , ".au" : "audio/basic"
156 | , ".avi" : "video/x-msvideo"
157 | , ".bat" : "application/x-msdownload"
158 | , ".bin" : "application/octet-stream"
159 | , ".bmp" : "image/bmp"
160 | , ".bz2" : "application/x-bzip2"
161 | , ".c" : "text/x-c"
162 | , ".cab" : "application/vnd.ms-cab-compressed"
163 | , ".cc" : "text/x-c"
164 | , ".chm" : "application/vnd.ms-htmlhelp"
165 | , ".class" : "application/octet-stream"
166 | , ".com" : "application/x-msdownload"
167 | , ".conf" : "text/plain"
168 | , ".cpp" : "text/x-c"
169 | , ".crt" : "application/x-x509-ca-cert"
170 | , ".css" : "text/css"
171 | , ".csv" : "text/csv"
172 | , ".cxx" : "text/x-c"
173 | , ".deb" : "application/x-debian-package"
174 | , ".der" : "application/x-x509-ca-cert"
175 | , ".diff" : "text/x-diff"
176 | , ".djv" : "image/vnd.djvu"
177 | , ".djvu" : "image/vnd.djvu"
178 | , ".dll" : "application/x-msdownload"
179 | , ".dmg" : "application/octet-stream"
180 | , ".doc" : "application/msword"
181 | , ".dot" : "application/msword"
182 | , ".dtd" : "application/xml-dtd"
183 | , ".dvi" : "application/x-dvi"
184 | , ".ear" : "application/java-archive"
185 | , ".eml" : "message/rfc822"
186 | , ".eps" : "application/postscript"
187 | , ".exe" : "application/x-msdownload"
188 | , ".f" : "text/x-fortran"
189 | , ".f77" : "text/x-fortran"
190 | , ".f90" : "text/x-fortran"
191 | , ".flv" : "video/x-flv"
192 | , ".for" : "text/x-fortran"
193 | , ".gem" : "application/octet-stream"
194 | , ".gemspec" : "text/x-script.ruby"
195 | , ".gif" : "image/gif"
196 | , ".gz" : "application/x-gzip"
197 | , ".h" : "text/x-c"
198 | , ".hh" : "text/x-c"
199 | , ".htm" : "text/html"
200 | , ".html" : "text/html"
201 | , ".ico" : "image/vnd.microsoft.icon"
202 | , ".ics" : "text/calendar"
203 | , ".ifb" : "text/calendar"
204 | , ".iso" : "application/octet-stream"
205 | , ".jar" : "application/java-archive"
206 | , ".java" : "text/x-java-source"
207 | , ".jnlp" : "application/x-java-jnlp-file"
208 | , ".jpeg" : "image/jpeg"
209 | , ".jpg" : "image/jpeg"
210 | , ".js" : "application/javascript"
211 | , ".json" : "application/json"
212 | , ".log" : "text/plain"
213 | , ".m3u" : "audio/x-mpegurl"
214 | , ".m4v" : "video/mp4"
215 | , ".man" : "text/troff"
216 | , ".mathml" : "application/mathml+xml"
217 | , ".mbox" : "application/mbox"
218 | , ".mdoc" : "text/troff"
219 | , ".me" : "text/troff"
220 | , ".mid" : "audio/midi"
221 | , ".midi" : "audio/midi"
222 | , ".mime" : "message/rfc822"
223 | , ".mml" : "application/mathml+xml"
224 | , ".mng" : "video/x-mng"
225 | , ".mov" : "video/quicktime"
226 | , ".mp3" : "audio/mpeg"
227 | , ".mp4" : "video/mp4"
228 | , ".mp4v" : "video/mp4"
229 | , ".mpeg" : "video/mpeg"
230 | , ".mpg" : "video/mpeg"
231 | , ".ms" : "text/troff"
232 | , ".msi" : "application/x-msdownload"
233 | , ".odp" : "application/vnd.oasis.opendocument.presentation"
234 | , ".ods" : "application/vnd.oasis.opendocument.spreadsheet"
235 | , ".odt" : "application/vnd.oasis.opendocument.text"
236 | , ".ogg" : "application/ogg"
237 | , ".p" : "text/x-pascal"
238 | , ".pas" : "text/x-pascal"
239 | , ".pbm" : "image/x-portable-bitmap"
240 | , ".pdf" : "application/pdf"
241 | , ".pem" : "application/x-x509-ca-cert"
242 | , ".pgm" : "image/x-portable-graymap"
243 | , ".pgp" : "application/pgp-encrypted"
244 | , ".pkg" : "application/octet-stream"
245 | , ".pl" : "text/x-script.perl"
246 | , ".pm" : "text/x-script.perl-module"
247 | , ".png" : "image/png"
248 | , ".pnm" : "image/x-portable-anymap"
249 | , ".ppm" : "image/x-portable-pixmap"
250 | , ".pps" : "application/vnd.ms-powerpoint"
251 | , ".ppt" : "application/vnd.ms-powerpoint"
252 | , ".ps" : "application/postscript"
253 | , ".psd" : "image/vnd.adobe.photoshop"
254 | , ".py" : "text/x-script.python"
255 | , ".qt" : "video/quicktime"
256 | , ".ra" : "audio/x-pn-realaudio"
257 | , ".rake" : "text/x-script.ruby"
258 | , ".ram" : "audio/x-pn-realaudio"
259 | , ".rar" : "application/x-rar-compressed"
260 | , ".rb" : "text/x-script.ruby"
261 | , ".rdf" : "application/rdf+xml"
262 | , ".roff" : "text/troff"
263 | , ".rpm" : "application/x-redhat-package-manager"
264 | , ".rss" : "application/rss+xml"
265 | , ".rtf" : "application/rtf"
266 | , ".ru" : "text/x-script.ruby"
267 | , ".s" : "text/x-asm"
268 | , ".sgm" : "text/sgml"
269 | , ".sgml" : "text/sgml"
270 | , ".sh" : "application/x-sh"
271 | , ".sig" : "application/pgp-signature"
272 | , ".snd" : "audio/basic"
273 | , ".so" : "application/octet-stream"
274 | , ".svg" : "image/svg+xml"
275 | , ".svgz" : "image/svg+xml"
276 | , ".swf" : "application/x-shockwave-flash"
277 | , ".t" : "text/troff"
278 | , ".tar" : "application/x-tar"
279 | , ".tbz" : "application/x-bzip-compressed-tar"
280 | , ".tcl" : "application/x-tcl"
281 | , ".tex" : "application/x-tex"
282 | , ".texi" : "application/x-texinfo"
283 | , ".texinfo" : "application/x-texinfo"
284 | , ".text" : "text/plain"
285 | , ".tif" : "image/tiff"
286 | , ".tiff" : "image/tiff"
287 | , ".torrent" : "application/x-bittorrent"
288 | , ".tr" : "text/troff"
289 | , ".txt" : "text/plain"
290 | , ".vcf" : "text/x-vcard"
291 | , ".vcs" : "text/x-vcalendar"
292 | , ".vrml" : "model/vrml"
293 | , ".war" : "application/java-archive"
294 | , ".wav" : "audio/x-wav"
295 | , ".wma" : "audio/x-ms-wma"
296 | , ".wmv" : "video/x-ms-wmv"
297 | , ".wmx" : "video/x-ms-wmx"
298 | , ".wrl" : "model/vrml"
299 | , ".wsdl" : "application/wsdl+xml"
300 | , ".xbm" : "image/x-xbitmap"
301 | , ".xhtml" : "application/xhtml+xml"
302 | , ".xls" : "application/vnd.ms-excel"
303 | , ".xml" : "application/xml"
304 | , ".xpm" : "image/x-xpixmap"
305 | , ".xsl" : "application/xml"
306 | , ".xslt" : "application/xslt+xml"
307 | , ".yaml" : "text/yaml"
308 | , ".yml" : "text/yaml"
309 | , ".zip" : "application/zip"
310 | }
311 | };
312 |
313 |
--------------------------------------------------------------------------------