Permalink
Browse files

First version of the sample forum application

  • Loading branch information...
1 parent 9a731c6 commit 9cdd40977fee75d8e2235a736b3207ed81128106 @antoviaque committed Mar 10, 2012
View
@@ -0,0 +1,3 @@
+server.log
+tutorial/_tutorial.html
+*~
View
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>Backbone-relational Tutorial</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.python.pydev.PyDevBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.python.pydev.pythonNature</nature>
+ </natures>
+</projectDescription>
View
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/Backbone-relational Tutorial</path>
+</pydev_pathproperty>
+</pydev_project>
View
140 app.js
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 Xavier Antoviaque <xavier@antoviaque.org>
+ *
+ * This software's license gives you freedom; you can copy, convey,
+ * propagate, redistribute and/or modify this program under the terms of
+ * the GNU Affero Gereral Public License (AGPL) as published by the Free
+ * Software Foundation (FSF), either version 3 of the License, or (at your
+ * option) any later version of the AGPL published by the FSF.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program in a file in the toplevel directory called
+ * "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Imports /////////////////////////////////////////////////////
+
+var restify = require('restify');
+var Logger = require('bunyan');
+
+
+// Database ////////////////////////////////////////////////////
+
+mongoose = require('mongoose');
+db = mongoose.connect('mongodb://localhost/forum');
+
+Thread = require('./models.js').Thread(db);
+Message = require('./models.js').Message(db);
+
+// Views ///////////////////////////////////////////////////////
+
+function get_thread(req, res, next) {
+ var send_result = function(err, thread_list) {
+ if (err) {
+ return next(err);
+ }
+
+ if(thread_list) {
+ if(thread_list.messages) {
+ thread_list.messages.sort(function(a, b) {
+ return ((a.date_added < b.date_added) ? -1 : (a.date_added > b.date_added) ? 1 : 0);
+ });
+ }
+ res.send(thread_list);
+ return next();
+ } else {
+ return next(new restify.ResourceNotFoundError("Could not find any such thread"));
+ }
+ };
+
+ if('_id' in req.params) {
+ Thread.findOne({'_id': req.params._id}, send_result);
+ } else {
+ Thread.find({}, send_result);
+ }
+}
+
+function post_thread(req, res, next) {
+ if(!req.body.title) {
+ return next(new restify.MissingParameterError("Missing required thread or message attribute in request body"));
+ }
+
+ new_thread = new Thread({title: req.body.title});
+ new_thread.save();
+ res.send(new_thread);
+
+ return next();
+}
+
+function post_message(req, res, next) {
+ if(!req.body.author || !req.body.text || !req.body.thread) {
+ return next(new restify.MissingParameterError("Missing required message attribute in request body"));
+ }
+
+ Thread.findOne({_id: req.body.thread}, function(err, thread) {
+ if (err) {
+ return next(err);
+ } else if(!thread) {
+ return next(new restify.ResourceNotFoundError("Could not find thread with id="+req.body.thread));
+ }
+
+ new_message = new Message({author: req.body.author,
+ text: req.body.text,
+ date_added: new Date()});
+ new_message.save();
+ thread.messages.push(new_message);
+ thread.save();
+
+ res.send(new_message);
+ return next();
+ })
+}
+
+
+// Server /////////////////////////////////////////////////////
+
+server = restify.createServer();
+
+server.use(restify.acceptParser(server.acceptable));
+server.use(restify.authorizationParser());
+server.use(restify.dateParser());
+server.use(restify.queryParser({ mapParams: false }));
+server.use(restify.bodyParser({ mapParams: false }));
+server.use(restify.throttle({
+ burst: 10,
+ rate: 1,
+ ip: false,
+ xff: true,
+}));
+
+// Logging
+server.on('after', restify.auditLogger({
+ log: new Logger({
+ name: 'mok',
+ streams: [{ level: "info", stream: process.stdout },
+ { level: "info", path: 'log/server.log' }],
+ })
+}));
+
+
+// Routes /////////////////////////////////////////////////////
+
+// Thread
+server.get('/api/thread/', get_thread);
+server.get('/api/thread/:_id', get_thread);
+server.post('/api/thread/', post_thread);
+
+// Message
+server.post('/api/message/', post_message);
+
+// Run ////////////////////////////////////////////////////////
+
+server.listen(3001, function() {
+ console.log('%s listening at %s', server.name, server.url);
+});
+
View
@@ -0,0 +1,30 @@
+server {
+
+ listen *:3000;
+
+ server_name localhost;
+
+ location ^~ /api/ {
+ proxy_read_timeout 60s;
+ proxy_send_timeout 60s;
+ proxy_pass http://127.0.0.1:3001;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ }
+
+ location ~* /thread/ {
+ rewrite .* /index.html last;
+ }
+
+ location / {
+ alias /home/antoviaque/prog/backbone-relational-tutorial/static/;
+ expires 3600; # 1h after request
+ }
+
+ # deny access to .htaccess files
+ #
+ location ~ /\.ht {
+ deny all;
+ }
+}
+
+
View
No changes.
View
@@ -0,0 +1,38 @@
+
+/**
+ * Module dependencies.
+ */
+
+var mongoose = require('mongoose')
+ , Schema = mongoose.Schema;
+
+/**
+ * Schema definition
+ */
+
+var Thread = new Schema({
+ title : String
+ , messages : [Message]
+});
+
+var Message = new Schema({
+ date_added : Date
+ , author : String
+ , text : String
+});
+
+
+/**
+ * Models
+ */
+
+mongoose.model('Thread', Thread);
+exports.Thread = function(db) {
+ return db.model('Thread');
+};
+
+mongoose.model('Message', Message);
+exports.Message = function(db) {
+ return db.model('Message');
+};
+
View
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 Xavier Antoviaque <xavier@antoviaque.org>
+ *
+ * This software's license gives you freedom; you can copy, convey,
+ * propagate, redistribute and/or modify this program under the terms of
+ * the GNU Affero Gereral Public License (AGPL) as published by the Free
+ * Software Foundation (FSF), either version 3 of the License, or (at your
+ * option) any later version of the AGPL published by the FSF.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program in a file in the toplevel directory called
+ * "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Global **********************************************************/
+
+html, body {
+ font-family: 'Helvetica';
+ font-size: 12px;
+ line-height: 130%;
+ color: #B9C2A8;
+ padding: 0;
+ margin: 0;
+ background-color: #d2543b;
+}
+
+
+a {
+ color: #B9C2A8;
+}
+
+.container {
+ width: 800px;
+ position: relative;
+ left: 50%;
+ margin-left: -400px;
+}
+
+.shadow {
+ position: absolute;
+ height: 100%;
+ width: 21px;
+}
+
+.shadow.left {
+ background: transparent url('../img/shadow_left.png') repeat left top;
+ top: 0;
+ left: 0;
+ margin-left: -21px;
+}
+
+.shadow.right {
+ background: transparent url('../img/shadow_right.png') repeat right top;
+ top: 0;
+ right: 0;
+ margin-right: -21px;
+}
+
+.main {
+ padding: 20px;
+ background-image: url('../img/green_bkg.png');mok
+}
+
+.centered {
+ text-align: center;
+}
+
+ul {
+ list-style-type: none;
+}
+
+.thread_list .thread_summary_view {
+ font-size: 15px;
+ font-weight: bold;
+ padding: 10px;
+ border-bottom: 1px solid;
+ border-color: grey;
+ cursor: pointer;
+ margin: 10px;
+ background-color: #6c8738;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ -ms-border-radius: 10px;
+ -o-border-radius: 10px;
+ border-radius: 10px;
+}
+
+.thread_list .thread_summary_view .thread_title {
+ font-weight: bold;
+}
+
+.thread_list .thread_summary_view .thread_nb_messages {
+ float: right;
+}
+
+input[type=text] {
+ padding: 5px;
+ width: 400px;
+ margin: 5px;
+}
+
+textarea {
+ width: 400px;
+ height: 200px;
+ margin: 5px;
+}
+
+.message_author {
+ font-weight: bold;
+ padding: 10px 0 0 5px;
+}
+
+.message_text {
+ font-size: 14px;
+ padding: 20px;
+}
+
+.message {
+ margin: 20px;
+ background-color: #6c8738;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ -ms-border-radius: 10px;
+ -o-border-radius: 10px;
+ border-radius: 10px;
+}
Oops, something went wrong.

0 comments on commit 9cdd409

Please sign in to comment.