History: Photo sharing service

Revision made 8 years ago by Francisco Presencia. Go to the last revision.

We are going to create a photo sharing service where the user is able to upload pictures and like them.

We will be using a simple MVC, so our file structure will be:

/controllers
  /users.js
  /photos.js
  /likes.js
/models
  /users.js # this will be half-mocked up since there's no time
            # or we'll use auth0 if possible
/views
  /index.pug
  /layout.pug
  /login.pug
  /photo.pug
/public
  /images
    /1.jpg
    /2.jpg
    ...
  /style.css
/.env
/.gitignore
/app.js
/package.json
/routes.js

The user will be able to login (actually no, we will fake this)

For this, first we set up our app.js:

require('dotenv').config();

var express = require('express');
var bodyparser = require('body-parser');
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;

db.once('open', function(){
  var app = express();
  app.set('view engine', 'pug');

  app.use(express.static('public'));
  app.use(bodyparser.urlencoded({ extended: false }));

  app.use(require('./routes'));

  app.listen(process.env.PORT || 3000);
});

In routes.js:

var express = require('express');
var router = express.Router();
var controllers = require('auto-load')('controllers');

router.get('/', controllers.photos.index);

module.exports = router;

Let's mock the data:

module.exports.index = function(req, res){
  res.render('index', {
    photos: [
      '/images/1.jpg',
      '/images/2.jpg',
      '/images/3.jpg',
      '/images/4.jpg',
      '/images/5.jpg'
    ]
  });
}

Install the dependencies

npm install formidable cloudinary --save

Then in our controllers/photos.js:

var formidable = require('formidable');
var cloudinary = require('cloudinary');

cloudinary.config({
  cloud_name: process.env.cloud,
  api_key: process.env.key,
  api_secret: process.env.secret
});

exports.upload = function(req, res, next){
  var form = new formidable.IncomingForm();
  form.parse(req, function (err, fields, files) {
    if (err) next(err);
    cloudinary.uploader.upload(files.image.path, function(result) {
      // Let's force https in the image:
      var image = result.url.replace('http://', 'https://');
      res.redirect('/');
    });
  });
});

Our layout.pug:

doctype html
html
  head
    title= title
    link(rel="stylesheet" href="https://cdn.jsdelivr.net/picnicss/6.0.0/plugins.min.css")
    link(rel='stylesheet', href='style.css')
    block head
  body
    block content

    script.
      document.addEventListener("DOMContentLoaded", function() {
        [].forEach.call(document.querySelectorAll('.dropimage'), function(img){
          img.onchange = function(e){
            var inputfile = this, reader = new FileReader();
            reader.onloadend = function(){
              inputfile.style['background-image'] = 'url('+reader.result+')';
            }
            reader.readAsDataURL(e.target.files[0]);
          }
        });
      });

In index.pug:

extends layout

block content

  h1= name

  div.flex.one.two-500.five-900
    each photo in photos
      div.photo
        img(src=photo)
        button.like Like

  form(action="/photos" method="POST" enctype="multipart/form-data")
    div.flex.two
      div
        label.dropimage
          input(type="file" name="image" title="Drop image or click me")
      div
        label Title:
          input(name="title" placeholder="Title")
        label Nickname:
          input(name="nick" placeholder="Nickname")
        input(type="submit" value="Upload")

In our style.css:

body {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 20px;
}

img {
  width: 100%;
}

New photos.js:

var models = require('auto-load')('models');

var formidable = require('formidable');
var cloudinary = require('cloudinary');

cloudinary.config({
  cloud_name: process.env.cloud,
  api_key: process.env.key,
  api_secret: process.env.secret
});

module.exports.index = function(req, res){
  models.photos.find({}, function(err, images){
    res.render('index', { photos: images });
  });
}

module.exports.upload = function(req, res, next){
  var form = new formidable.IncomingForm();

  function redirect(err){
    if (err) next(err);
    res.redirect('/');
  }

  function parseForm(err, fields, files) {
    if (err) next(err);
    cloudinary.uploader.upload(files.image.path, function (result) {
      // Let's force https in the image:
      var url = result.url.replace('http://', 'https://');

      var Photo = new models.photos({
        url: url,
        title: fields.title,
        nickname: fields.nick,
      });
      Photo.save(redirect);
    });
  }

  form.parse(req, parseForm);
}

The model.js:

var mongoose = require('mongoose');

var photosSchema = mongoose.Schema({
  title: { type: String, required: true },
  nickname: { type: String, unique: true, required: true },
  url: { type: String, required: true },
  likes: [{ type: String }]
});

module.exports = mongoose.model('Photos', photosSchema);

Auth

We will follow Auth0 tutorial.