Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
# photo_collection
Backend For a Cloud Photo Storage app

## Development

```sh
npm install
npm test
npm start
```

The server uses these optional environment variables:

- `PORT` (defaults to `3030`)
- `MYSQL_HOST` (defaults to `127.0.0.1`)
- `MYSQL_USER` (defaults to `root`)
- `MYSQL_PASSWORD` (defaults to an empty password)
- `MYSQL_DATABASE` (defaults to `photo_collection`)
24 changes: 24 additions & 0 deletions lib/http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
var qs = require('querystring');

exports.readFormBody = function(req, callback) {
var body = '';
req.on('data', function(chunk) {
body += chunk;
if (body.length > 1e6) {
req.destroy();
}
});
req.on('end', function() {
callback(qs.parse(body));
});
};

exports.sendJson = function(resp, statusCode, payload) {
resp.statusCode = statusCode;
resp.setHeader('Content-Type', 'application/json');
resp.end(JSON.stringify(payload));
};

exports.sendError = function(resp, statusCode, message) {
exports.sendJson(resp, statusCode, { error: message });
};
43 changes: 43 additions & 0 deletions lib/static-images.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
var path = require('path');

var imageRoot = path.resolve(__dirname, '..', 'public', 'images');
var contentTypes = {
'.gif': 'image/gif',
'.jpeg': 'image/jpeg',
'.jpg': 'image/jpeg',
'.png': 'image/png',
'.webp': 'image/webp'
};

exports.imageRoot = imageRoot;

exports.resolveImagePath = function(pathname) {
var prefix = '/public/images/';
if (pathname.indexOf(prefix) !== 0) {
return null;
}

var relativePath;
try {
relativePath = decodeURIComponent(pathname.slice(prefix.length));
} catch (err) {
if (err instanceof URIError) {
return null;
}
throw err;
}
if (!relativePath || relativePath.indexOf('\0') !== -1) {
return null;
}

var resolvedPath = path.resolve(imageRoot, relativePath);
if (resolvedPath !== imageRoot && resolvedPath.indexOf(imageRoot + path.sep) === 0) {
return resolvedPath;
}

return null;
};

exports.contentTypeFor = function(filePath) {
return contentTypes[path.extname(filePath).toLowerCase()] || 'application/octet-stream';
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "node test/static-images.test.js",
"start": "node server.js"
},
"author": "",
Expand Down
103 changes: 64 additions & 39 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
var http = require('http');
var url = require('url');
var join = require('path').join;
var qs = require('querystring');
var path = require('path');
var fs = require('fs');
var mkdirp = require('mkdirp');
var formidable = require('formidable');
var fileServer = require('./lib/fileserver.js');
var httpHelpers = require('./lib/http.js');
var staticImages = require('./lib/static-images.js');
var mysql = require('mysql');
var port = process.env.PORT || 3030;
var db = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: '',
database: 'photo_collection'
host: process.env.MYSQL_HOST || '127.0.0.1',
user: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWORD || '',
database: process.env.MYSQL_DATABASE || 'photo_collection'
});
db.connect(function(err) {
if(err)throw err;
Expand All @@ -22,9 +24,21 @@ http.createServer(function(req, resp) {
case 'GET':
var new_uri = url.parse(req.url);
if(new_uri.pathname.indexOf('/public/images')== 0){
// console.log(new_uri);
fs.createReadStream('.'+new_uri.pathname).pipe(resp);
// resp.end("a phto");
var imagePath = staticImages.resolveImagePath(new_uri.pathname);
if (!imagePath) {
httpHelpers.sendError(resp, 400, 'Invalid image path');
return;
}

fs.stat(imagePath, function(err, stat) {
if (err || !stat.isFile()) {
httpHelpers.sendError(resp, 404, 'Image not found');
return;
}

resp.setHeader('Content-Type', staticImages.contentTypeFor(imagePath));
fs.createReadStream(imagePath).pipe(resp);
});
return;
}
if(new_uri.pathname == '/api' ){
Expand All @@ -33,14 +47,17 @@ http.createServer(function(req, resp) {
fs.createReadStream('./public/form.html').pipe(resp);
return;
}
var para = new_uri.query.split('&');
// console.log(para[0].split('='));
// console.log(para[1].split('='));
db.query("select * from users where email = ?", para[0].split('=')[1], function (err, row) {
var queryParameters = url.parse(req.url, true).query;
if (!queryParameters.email || !queryParameters.password) {
httpHelpers.sendError(resp, 400, 'email and password are required');
return;
}

db.query("select * from users where email = ?", queryParameters.email, function (err, row) {
if(err) throw err;
console.log(row);
if(row.length == 0){
fileServer.addUser(db, para[0].split('=')[1], para[1].split('=')[1]);
fileServer.addUser(db, queryParameters.email, queryParameters.password);
resp.end('New Acccount');
}else{
resp.setHeader('exist_before', true);
Expand All @@ -51,17 +68,15 @@ http.createServer(function(req, resp) {
}
break;
case 'POST':
var body = '';
var new_post_uri = url.parse(req.url);
if(new_post_uri.pathname == '/addcollection'){
// console.log(new_post_uri);
body = '';
req.on('data', function(chunk) {
body += chunk;
});
req.on('end', function() {
var parameters = qs.parse(body);
httpHelpers.readFormBody(req, function(parameters) {
// console.log(parameters);
if (!parameters.collection_name || !parameters.user_no) {
httpHelpers.sendError(resp, 400, 'collection_name and user_no are required');
return;
}
db.query("insert into collections (collection_name, user_no) values (?,?)", [parameters.collection_name, parameters.user_no], function (err, row) {
if(err) throw err;
var jsonresp = fileServer.listCollection(db, parameters.user_no, resp);
Expand All @@ -72,26 +87,24 @@ http.createServer(function(req, resp) {
}
if(new_post_uri.pathname == '/login'){
// console.log(new_post_uri);
body = '';
req.on('data', function(chunk) {
body += chunk;
});
req.on('end', function() {
var parameters = qs.parse(body);
httpHelpers.readFormBody(req, function(parameters) {
// console.log(parameters);
if (!parameters.email || !parameters.password) {
httpHelpers.sendError(resp, 400, 'email and password are required');
return;
}
fileServer.login(db, parameters, resp);
});
return;
}
if(new_post_uri.pathname == '/listcollection'){
// console.log(new_post_uri);
body = '';
req.on('data', function(chunk) {
body += chunk;
});
req.on('end', function() {
var parameters = qs.parse(body);
httpHelpers.readFormBody(req, function(parameters) {
// console.log(parameters);
if (!parameters.user_no) {
httpHelpers.sendError(resp, 400, 'user_no is required');
return;
}
fileServer.listCollection(db, parameters.user_no, resp);
});
return;
Expand All @@ -112,17 +125,30 @@ http.createServer(function(req, resp) {
form.parse(req, function (err, fields, files) {
if(err)throw err;
// console.log("Successfully formidable");
if (!fields.file_details || !files.uploaded_file) {
httpHelpers.sendError(resp, 400, 'file_details and uploaded_file are required');
return;
}

var collectionDirectory = String(fields.file_details).replace(/[^a-zA-Z0-9_-]/g, '');
if (!collectionDirectory) {
httpHelpers.sendError(resp, 400, 'file_details is invalid');
return;
}

db.query("select * from photographs where collection_number = ?", fields.file_details, function (err, row) {
if(err) throw err;
// console.log(row);
var new_url = "public/images/"+fields.file_details;
var new_url = "public/images/"+collectionDirectory;
mkdirp(new_url, function (err) {
if(err)throw err;
// console.log("Successfully made dir");
fs.rename(files.uploaded_file.path, new_url+"/"+row.length+".jpg", function (err) {
var extension = path.extname(files.uploaded_file.name).toLowerCase() || '.jpg';
var fileName = Date.now() + '-' + row.length + extension;
fs.rename(files.uploaded_file.path, new_url+"/"+fileName, function (err) {
if (err) throw err;
// console.log("Successfully Uploaded");
fileServer.addPhoto(db, new_url+"/"+row.length+".jpg", fields.file_details, resp);
fileServer.addPhoto(db, new_url+"/"+fileName, fields.file_details, resp);
});
// console.log(files.uploaded_file.name);
});
Expand Down Expand Up @@ -179,8 +205,7 @@ http.createServer(function(req, resp) {
default:
break;
}
}).listen(3030 || process.env.PORT, function() {
console.log("Listening to 3030");
}).listen(port, function() {
console.log("Listening to "+port);
});
fileServer.printcwd("runing");

18 changes: 18 additions & 0 deletions test/static-images.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
var assert = require('assert');
var path = require('path');
var staticImages = require('../lib/static-images');

var safePath = staticImages.resolveImagePath('/public/images/1/photo.jpg');
assert.strictEqual(
safePath,
path.join(staticImages.imageRoot, '1', 'photo.jpg')
);

assert.strictEqual(staticImages.resolveImagePath('/public/images/../server.js'), null);
assert.strictEqual(staticImages.resolveImagePath('/public/images/%2e%2e/server.js'), null);
assert.strictEqual(staticImages.resolveImagePath('/public/images/%'), null);
assert.strictEqual(staticImages.resolveImagePath('/public/images/'), null);
assert.strictEqual(staticImages.contentTypeFor('/tmp/photo.PNG'), 'image/png');
assert.strictEqual(staticImages.contentTypeFor('/tmp/photo.txt'), 'application/octet-stream');

console.log('static image tests passed');