back end - GiiovanniK/blok-tech GitHub Wiki

Inhoudsopgave

Belangrijkste code concepten

DB Connection & DB Modellen

Belangrijke variabelen, zoals de DB uri worden opgeslagen in een .env bestand. Dit bestand wordt niet mee gecommit naar GitHub, want deze staat in de .gitignore. Dit zorgt er voor dat deze variabelen altijd secret blijven. Vervolgens exporteer ik de functie connectDB om te kunnen requiren in andere bestanden.

require('dotenv').config();
const uri = process.env.ATLAS_URI;

const connectDB = async (mongoose) => {
    try {
        await mongoose.connect(uri, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            useFindAndModify: false,
            useCreateIndex: true
        });
        console.log('Connection to DB successful');
    } catch (err) {
        console.log('Connection to DB failed');
    }
};

module.exports = connectDB;

Mongoose DB modellen. Ik heb twee schema's aangemaakt voor twee verschillende functies. Login en user uploads. De modellen zijn opgesteld met mongoose en deze geven aan hoe de structuur van een collectie er uit moet zien.

const mongoose = require('mongoose');
require('../connect.js')

const UserSchema = new mongoose.Schema({
    email: {
        type: String,
        required: true
    },
    username: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true
    }
});

const User = mongoose.model('users', UserSchema);

module.exports = User;
const mongoose = require('mongoose');
require('../connect.js')

const userUploadSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    gender: {
        type: String,
        required: true
    },
    skill: {
        type: [String],
        required: true
    },
    genre: {
        type: [String],
        required: true
    },
    createdAt: {
        type: Date, 
        default: Date.now,
        required: true
    }
});

const userUpload = mongoose.model('useruploads', userUploadSchema);

module.exports = userUpload;

Routes

Alle routes in de app, waar de gebruiker naar toe kan navigeren. De routes zijn. Home(/), About, Login, Register, Dashboard, changePassword en userUpload. De title option wordt gebruikt om een pagina <title> in de browser te tonen.

app.get('/', (req, res) => {
    res.render('index', {
        title: 'Home'
    });
});

app.get('/about', (req, res) => {
    res.render('about', {
        title: 'About'
    });
});

app.get('/login', checkNoAuth.checkNotAuthenticated, (req, res) => {
    res.render('login', {
        title: 'Login'
    })
});

app.get('/register', checkNoAuth.checkNotAuthenticated, (req, res) => {
    res.render('register', {
        title: 'Register'
    })
});

app.get('/dashboard', checkAuth.checkAuthenticated, async (req, res) => {
    const userupload = await userUpload.find().select('username gender skill genre');
    res.render('dashboard', {
        username: req.user.username,
        title: 'Dashboard',
        data: userupload
    });
});

app.get('/changePassword', checkNoAuth.checkAuthenticated, (req, res) => {
    res.render('changePassword', {
        title: 'Change Password'
    })
});

app.get('/userUpload', checkNoAuth.checkAuthenticated, (req, res) => {
    res.render('userUpload', {
        title: 'Create Listing'
    })
});

Als een route niet bestaat krijgt de gebruiker een HTTP 404 status, oftewel, de pagina bestaat niet.

app.get('*', (req, res) => {
    res.status(404).send('This page does not exist!');
});

Session

Ik heb gebruik gemaakt van session d.m.v. de package passport. Deze session zorgt ervoor dat er gecheckt kan worden of er een gebruiker is ingelogd of uitgelogd, waardoor er bepaalde content alleen beschikbaar kan worden gesteld voor een ingelogde of uitgelogde gebruiker.

const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const User = require('../models/User.js');

module.exports = (passport) => {
    passport.use(
        new LocalStrategy({
            usernameField: 'email'
        }, (email, password, done) => {
            User.findOne({
                    email: email
                })
                .then(user => {
                    if (!user)
                        return done(null, false, {
                            message: 'Invalid email'
                        });

                    bcrypt.compare(password, user.password, (err, isMatch) => {
                        if (err) throw err;

                        if (isMatch) {
                            return done(null, user);
                        } else {
                            return done(null, false, {
                                message: 'Invalid password'
                            })
                        }
                    });
                }).catch(err => console.log(err));
        })
    );

    passport.serializeUser((user, done) => {
        done(null, user.id);
    });
    passport.deserializeUser((id, done) => {
        User.findById(id, (err, user) => {
            done(err, user);
        });
    });
}

In de auth.js maak ik functies aan die bepaalde routes kunnen blokkeren voor een ingelogde of uitgelogde gebruiker. De checkAuthenticated checkt of een gebruiker is ingelogd, mocht dit niet zo zijn wordt de gebruiker terug gestuurd naar de /login route om in te kunnen loggen. de checkNotAuthenticated checkt of een gebruiker nog steeds is ingelogd. Mocht dit het geval zijn dan kan de gebruiker niet navigeren naar de routes /login en /register. De gebruiker wordt terug gestuurd naar de homepage van een ingelogde gebruiker, namelijk /dashboard.

const checkAuthenticated = (req, res, next) => {
    if (req.isAuthenticated()) {
        return next();
    }
    // req.flash('error_msg', 'Please log in to view this page.');
    res.redirect('login');
}

const checkNotAuthenticated = (req, res, next) => {
    if (req.isAuthenticated()) {
        res.redirect('dashboard');
    } else
        return next();
}

module.exports = {
    checkAuthenticated,
    checkNotAuthenticated
};

CRUD Operations

Find

Bij het wachtwoord veranderen wordt de User in de DB vergeleken met de user die ingelogd d.m.v. het checken van de user in de huidige session.

app.post('/changePassword', async (req, res) => {
    try {
        const salt = await bcrypt.genSalt()
        const hashedPassword = await bcrypt.hash(req.body.password, salt)
        const user = await User.findOne({
            username: req.user.username
        });
        await User.updateOne(user, {
            password: hashedPassword
        });
        await user.save()
        res.redirect('login')
    } catch {
        res.status(500).send();
    }
});

Voor het ophalen van de geuploade user data moet er uit de database velden geselecteerd worden die ik wil renderen. In dit geval zijn dat de velden: username, gender, skill en genre.

app.get('/dashboard', checkAuth.checkAuthenticated, async (req, res) => {
    const userupload = await userUpload.find().select('username gender skill genre');
    res.render('dashboard', {
        username: req.user.username,
        title: 'Dashboard',
        data: userupload
    });
});

Update

Een wachtwoord kan veranderd worden. Dit wachtwoord wordt geupdate in de DB. Deze wordt ook opnieuw gehashed, zodat het wachtwoord veilig blijft.

app.post('/changePassword', async (req, res) => {
    try {
        const salt = await bcrypt.genSalt()
        const hashedPassword = await bcrypt.hash(req.body.password, salt)
        const user = await User.findOne({
            username: req.user.username
        });
        await User.updateOne(user, {
            password: hashedPassword
        });
        await user.save()
        res.redirect('login')
    } catch {
        res.status(500).send();
    }
});

Issues

Week 1

Video's bekeken over NPM packages. Hierna wat rondgekeken op NPM library website waarbij ik even heb gekeken naar alle verschillende packages. Vervolgens Vue.js modal (https://www.npmjs.com/package/vue-js-modal) en nodemon (als dev dependency) (https://www.npmjs.com/package/nodemon) geïnstalleerd. Ook een start runscript gemaakt "start": "node index.js". Hierna nog een algemene node .gitignore file gepushed naar GitHub met gebruik van de terminal.

Week 2

Met ExpressJS de server opgezet. Routes aangemaakt, voor nu root (index), about en login. Vervolgens ingelezen over Handlebars om deze toe te gaan passen als templating engine, moest hiervoor nog een viewengine package installeren. index.hbs, about.hbs en login.hbs files aangemaakt. Hier is ook naar toe te navigeren. Header en footer files aangemaakt om deze op een later punt te kunnen includen op de dynamische pagina's.

Week 3

POST request oefeningen en ook connectie met DB gemaakt en getest door met een script data te importeren.

Week 4

Inlog/registreer systeem, waarbij input wordt gestored in de DB en ook weer opgehaald. Change Password functie waarbij wachtwoord geupdate wordt in de DB (met ook weer de benodigde hashing).

Bronnen

github/gitignore. GitHub. https://github.com/github/gitignore/blob/master/Node.gitignore

Documentation. Node.Js. https://nodejs.org/en/docs/

Handlebars. (n.d.). Handlebars. https://handlebarsjs.com/

Get Started with Atlas — MongoDB Atlas. (n.d.). MongoDB Atlas. https://docs.atlas.mongodb.com/getting-started/

Quick Start — Node.js. (n.d.). Quick Start - Node.Js. https://docs.mongodb.com/drivers/node/quick-start/

Mongoose ODM v5.11.19. (n.d.). Mongoose Documentation. https://mongoosejs.com/

MongoDB CRUD Operations — MongoDB Manual. (n.d.). MongoDB CRUD Operations. https://docs.mongodb.com/manual/crud/

npm: bcrypt. (2021, February 26). Npm - Bcrypt. https://www.npmjs.com/package/bcrypt

npm: express-handlebars. (2021, February 16). Npm. https://www.npmjs.com/package/express-handlebars

⚠️ **GitHub.com Fallback** ⚠️