From 19987246ad389745efaeac6408a0bec6a710de91 Mon Sep 17 00:00:00 2001 From: lenanex Date: Tue, 16 Feb 2016 10:59:08 +0100 Subject: [PATCH] Init Initial post --- lib/globals.js | 11 ++++ lib/model/model-albums.js | 75 +++++++++++++++++++++++++ lib/model/model-photos.js | 64 +++++++++++++++++++++ lib/model/model-users.js | 89 +++++++++++++++++++++++++++++ package.json | 18 ++++++ public/stylesheets/style.css | 8 +++ routes/albums.js | 62 ++++++++++++++++++++ routes/index.js | 9 +++ routes/photos.js | 65 +++++++++++++++++++++ routes/users.js | 93 ++++++++++++++++++++++++++++++ server.js | 77 +++++++++++++++++++++++++ setup/photoalbums.sql | 106 +++++++++++++++++++++++++++++++++++ views/error.jade | 6 ++ views/index.jade | 5 ++ views/layout.jade | 7 +++ 15 files changed, 695 insertions(+) create mode 100644 lib/globals.js create mode 100644 lib/model/model-albums.js create mode 100644 lib/model/model-photos.js create mode 100644 lib/model/model-users.js create mode 100644 package.json create mode 100644 public/stylesheets/style.css create mode 100644 routes/albums.js create mode 100644 routes/index.js create mode 100644 routes/photos.js create mode 100644 routes/users.js create mode 100644 server.js create mode 100644 setup/photoalbums.sql create mode 100644 views/error.jade create mode 100644 views/index.jade create mode 100644 views/layout.jade diff --git a/lib/globals.js b/lib/globals.js new file mode 100644 index 0000000..406ef78 --- /dev/null +++ b/lib/globals.js @@ -0,0 +1,11 @@ +module.exports = { + applicationPort : 80, + database : + { + host : 'localhost', + port : 8889, + database : 'photoalbums', + user : 'root', + password : 'root' + } +} \ No newline at end of file diff --git a/lib/model/model-albums.js b/lib/model/model-albums.js new file mode 100644 index 0000000..c7e8294 --- /dev/null +++ b/lib/model/model-albums.js @@ -0,0 +1,75 @@ +var mysql = require('mysql'); +var globals = require('./../globals'); +var connection = mysql.createConnection(globals.database); + +function createAlbum(params, callback){ + var query = 'INSERT INTO albums SET ? '; + connection.query(query, params, function(err, rows, fields){ + if(err){ + callback(err); + } else { + var response = { + id : rows.insertId, + title : params.title + }; + callback(null, response); + } + }); +} + +function getAlbumsByUser(params, callback){ + var query = 'SELECT albumID, title FROM albums WHERE userID=' + connection.escape(params.userID); + connection.query(query, function(err, rows, fields){ + if(err){ + callback(err); + } else { + callback(null, rows); + } + }); +} + +function getAlbumByID(params, callback){ + var query = 'SELECT title, albumID, userID FROM albums WHERE albumID=' + connection.escape(params.albumID); + connection.query(query, function(err, rows, fields){ + if(rows.length > 0){ + getPhotosForAlbum(rows[0], function(err, obj){ + if(err){ + callback(err); + } else { + callback(null, obj); + } + }); + } else { + callback(null, []); + } + }); +} + +function deleteAlbum(params, callback){ + var query = 'UPDATE albums SET published=0 WHERE albumID=' + connection.escape(params.albumID); + connection.query(query, function(err, rows, fields){ + if(err){ + callback(err); + } else { + callback(null, {message: 'Album deleted successfully'}); + } + }); +} + +function getPhotosForAlbum(album, callback){ + var modelPhotos = require('./model-photos'); + modelPhotos.getPhotosByAlbumID(album, function(err, obj){ + if(err){ + callback(err); + } else { + album.photos = obj; + callback(null, album); + } + }); +} + +exports.createAlbum = createAlbum; +exports.deleteAlbum = deleteAlbum; +exports.getAlbumsByUser = getAlbumsByUser; +exports.getAlbumByID = getAlbumByID; + diff --git a/lib/model/model-photos.js b/lib/model/model-photos.js new file mode 100644 index 0000000..6d781f6 --- /dev/null +++ b/lib/model/model-photos.js @@ -0,0 +1,64 @@ +var mysql = require('mysql'); +var globals = require('./../globals'); +var connection = mysql.createConnection(globals.database); + +function createPhoto(params, callback){ + var query = 'INSERT INTO photos SET ? '; + connection.query(query, params, function(err, rows, fields){ + if(err){ + callback(err); + } else { + var response = { + id : rows.insertId + }; + callback(null, response); + } + }); + +} + +function getPhotoByID(params, callback){ + var query = 'SELECT photoID, caption, albumID, userID FROM photos WHERE published=1 AND photoID=' + connection.escape(params.photoID); + connection.query(query, function(err, rows, fields){ + if(err){ + callback(err); + } else { + if(rows.length > 0){ + callback(null, rows); + } else { + callback(null, []); + } + } + }); +} + +function getPhotosByAlbumID(params, callback){ + var query = 'SELECT photoID, caption, albumID, userID FROM photos WHERE published=1 AND albumID=' + connection.escape(params.albumID); + connection.query(query, function(err, rows, fields){ + if(err){ + callback(err); + } else { + if(rows.length > 0){ + callback(null, rows); + } else { + callback(null, []); + } + } + }); +} + +function deletePhotoByID(params, callback){ + var query = 'UPDATE photos SET published=0 WHERE photoID=' + connection.escape(params.photoID); + connection.query(query, function(err, rows, fields){ + if(rows.length > 0){ + callback(null, rows); + } else { + callback(null, []); + } + }); +} + +exports.createPhoto = createPhoto; +exports.getPhotoByID = getPhotoByID; +exports.getPhotosByAlbumID = getPhotosByAlbumID; +exports.deletePhotoByID = deletePhotoByID; \ No newline at end of file diff --git a/lib/model/model-users.js b/lib/model/model-users.js new file mode 100644 index 0000000..9074203 --- /dev/null +++ b/lib/model/model-users.js @@ -0,0 +1,89 @@ +var mysql = require('mysql'); +var globals = require('./../globals'); +var connection = mysql.createConnection(globals.database); + +function getAllUsers(callback){ + connection.query('SELECT username, userID FROM users', function(err, rows, fields){ + if(err){ + callback(err); + } else { + callback(null, rows); + } + }); +} + +function getUser(params, callback){ + connection.query('SELECT username, userID FROM users WHERE username=' + connection.escape(params.username), function(err, rows, fields){ + if(err){ + callback(err); + } else { + if(rows.length > 0){ + var userObject = rows[0]; + var modelAlbums = require('./model-albums'); + modelAlbums.getAlbumsByUser({userID: userObject.userID}, function(err, obj){ + if(err){ + callback(err); + } else { + userObject.albums = obj; + callback(null, userObject); + } + }); + } else { + callback(null, []); + } + } + }); +} + +function createUser(params, callback){ + + var newUser = { + username: params.username, + password: params.password, + email: params.email + } + + var query = 'INSERT INTO users SET ? '; + + connection.query(query, newUser, function(err, rows, fields) { + if (err) { + if(err.errno == 1062){ + var error = new Error("This username has already been taken."); + callback(error); + } else { + callback(err); + } + } else { + callback(null, {message:'Registration successful!'}); + } + }); + +} + +function loginUser(params, callback){ + connection.query('SELECT username, password, userID FROM users WHERE username=' + connection.escape(params.username), function(err, rows, fields) { + if(err){ + callback(err); + } else if(rows.length > 0){ + var response = { + username: rows[0].username, + userID: rows[0].userID + } + callback(null, response); + } else { + var error = new Error("Invalid login"); + callback(error); + } + }); +} + + +function logoutUser(params, callback){ + callback({message: 'You have logged out successfully'}); +} + +exports.getAllUsers = getAllUsers; +exports.getUser = getUser; +exports.createUser = createUser; +exports.loginUser = loginUser; +exports.logoutUser = logoutUser; diff --git a/package.json b/package.json new file mode 100644 index 0000000..645a099 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "photoalbums", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "~4.8.6", + "body-parser": "~1.6.6", + "cookie-parser": "~1.3.2", + "mysql": "2.0.*", + "morgan": "~1.2.3", + "serve-favicon": "~2.0.1", + "debug": "~1.0.4", + "jade": "~1.5.0" + } +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..30e047d --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} \ No newline at end of file diff --git a/routes/albums.js b/routes/albums.js new file mode 100644 index 0000000..069f542 --- /dev/null +++ b/routes/albums.js @@ -0,0 +1,62 @@ +var express = require('express'); +var router = express.Router(); +var model = require('./../lib/model/model-albums'); + +/* GET album by ID */ +router.get('/id/:albumID', function(req, res) { + if(req.param('albumID')){ + var params = { + albumID : req.param('albumID') + } + model.getAlbumByID(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid album ID'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid album ID'}); + } +}); + +/* POST create album. */ +router.post('/upload', function(req, res) { + if(req.param('title') && req.param('userID')){ + var params = { + userID : req.param('userID'), + title : req.param('title') + } + model.createAlbum(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid album data'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid album data'}); + } +}); + +/* POST delete album. */ +router.post('/delete', function(req, res) { + if(req.param('albumID')){ + var params = { + albumID : req.param('albumID') + } + model.deleteAlbum(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Album not found'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid album ID'}); + } +}); + + + +module.exports = router; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..bd64d6f --- /dev/null +++ b/routes/index.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res) { + res.render('index', { title: 'Photoalbums' }); +}); + +module.exports = router; diff --git a/routes/photos.js b/routes/photos.js new file mode 100644 index 0000000..7e4f288 --- /dev/null +++ b/routes/photos.js @@ -0,0 +1,65 @@ +var express = require('express'); +var router = express.Router(); +var model = require('./../lib/model/model-photos'); + +/* GET photo by ID */ +router.get('/id/:id', function(req, res) { + if(req.param('id')){ + var params = { + photoID : req.param('id') + } + model.getPhotoByID(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid photo ID'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid login'}); + } +}); + +/* POST create photo. */ +router.post('/upload', function(req, res) { + if(req.param('albumID') && req.param('userID')){ + var params = { + userID : req.param('userID'), + albumID : req.param('albumID') + } + if(req.param('caption')){ + params.caption = req.param('caption'); + } + + model.createPhoto(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid photo data'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid photo data'}); + } +}); + +/* POST delete photo. */ +router.post('/delete', function(req, res) { + if(req.param('id')){ + var params = { + photoID : req.param('id') + } + model.deletePhoto(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Photo not found'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid photo ID'}); + } +}); + + +module.exports = router; diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..61f4217 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,93 @@ +var express = require('express'); +var router = express.Router(); +var model = require('./../lib/model/model-users'); + +/* GET users listing. */ +router.get('/', function(req, res) { + model.getAllUsers(function(err, obj){ + if(err){ + res.status(500).send({error: 'An unknown server error has occurred!'}); + } else { + res.send(obj); + } + }) +}); + + +/* GET albums by user */ +router.get('/user/:user', function(req, res) { + var params= { + username: req.param('user') + } + model.getUser(params, function(err, obj){ + if(err){ + res.status(500).send({error: 'An unknown server error has occurred!'}); + } else { + res.send(obj); + } + }); +}); + +/* POST user login. */ +router.post('/login', function(req, res) { + if(req.param('username') && req.param('password') ){ + var params = { + username: req.param('username').toLowerCase(), + password: req.param('password') + }; + + model.loginUser(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid login'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid login'}); + } +}); + +/* POST user logout. */ +router.post('/logout', function(req, res) { + if(req.param('userID')){ + model.logoutUser({}, function(err, obj){ + if(err){ + res.status(400).send({error: 'Invalid user'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid user'}); + } +}); + +/* POST user registration. */ +router.post('/register', function(req, res) { + if(req.param('username') && req.param('password') && req.param('email')){ + var email = unescape(req.param('email')); + var emailMatch = email.match(/\S+@\S+\.\S+/); + if (emailMatch !== null) { + var params = { + username: req.param('username').toLowerCase(), + password: req.param('password'), + email: req.param('email').toLowerCase() + }; + + model.createUser(params, function(err, obj){ + if(err){ + res.status(400).send({error: 'Unable to register'}); + } else { + res.send(obj); + } + }); + } else { + res.status(400).send({error: 'Invalid email'}); + } + } else { + res.status(400).send({error: 'Missing required field'}); + } +}); + +module.exports = router; diff --git a/server.js b/server.js new file mode 100644 index 0000000..beed5d9 --- /dev/null +++ b/server.js @@ -0,0 +1,77 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +var debug = require('debug')('photoalbums'); +var routes = require('./routes/index'); +var users = require('./routes/users'); +var photos = require('./routes/photos'); +var albums = require('./routes/albums'); +var globals = require('./lib/globals'); +var mysql = require('mysql'); +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(__dirname + '/public/favicon.ico')); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', routes); +app.use('/users', users); +app.use('/photos', photos); +app.use('/albums', albums); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render({ + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +app.set('port', globals.applicationPort); + +var server = app.listen(app.get('port'), function() { + debug('Express server listening on port ' + server.address().port); + var connection = mysql.createConnection(globals.database); + connection.connect(function(err) { + if(err){ + console.log('error connecting to database:'); + } else { + console.log('connected to database!'); + } + }); +}); \ No newline at end of file diff --git a/setup/photoalbums.sql b/setup/photoalbums.sql new file mode 100644 index 0000000..4788628 --- /dev/null +++ b/setup/photoalbums.sql @@ -0,0 +1,106 @@ +-- phpMyAdmin SQL Dump +-- version 4.2.10 +-- http://www.phpmyadmin.net +-- +-- Host: localhost:8889 +-- Generation Time: Nov 06, 2014 at 10:09 PM +-- Server version: 5.5.38 +-- PHP Version: 5.6.2 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +-- +-- Database: `photoalbums` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `albums` +-- + +CREATE TABLE `albums` ( +`albumID` int(11) NOT NULL, + `userID` int(11) NOT NULL, + `published` int(11) NOT NULL DEFAULT '1', + `title` varchar(50) NOT NULL DEFAULT '' +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `photos` +-- + +CREATE TABLE `photos` ( +`photoID` int(11) NOT NULL, + `userID` int(11) NOT NULL, + `published` int(11) NOT NULL DEFAULT '1', + `albumID` int(11) NOT NULL, + `caption` varchar(250) NOT NULL +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +CREATE TABLE `users` ( +`userID` int(11) NOT NULL, + `username` varchar(50) NOT NULL, + `email` varchar(50) NOT NULL, + `password` varchar(50) NOT NULL DEFAULT 'password' +) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `albums` +-- +ALTER TABLE `albums` + ADD PRIMARY KEY (`albumID`); + +-- +-- Indexes for table `photos` +-- +ALTER TABLE `photos` + ADD PRIMARY KEY (`photoID`); + +-- +-- Indexes for table `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`userID`), ADD UNIQUE KEY `username` (`username`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `albums` +-- +ALTER TABLE `albums` +MODIFY `albumID` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=8; +-- +-- AUTO_INCREMENT for table `photos` +-- +ALTER TABLE `photos` +MODIFY `photoID` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=6; +-- +-- AUTO_INCREMENT for table `users` +-- +ALTER TABLE `users` +MODIFY `userID` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=17; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..3d63b9a --- /dev/null +++ b/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..b945f57 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content \ No newline at end of file