Working backend setup with endpoints and database setup for adventures.

This commit is contained in:
2019-02-18 22:42:52 +01:00
parent ba1070d5b2
commit cfd0b88987
26 changed files with 3083 additions and 0 deletions

9
.gitignore vendored
View File

@@ -1,3 +1,12 @@
# --- Javascript ignore ---
node_modules
.env
# --- Python ignore ---
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

8
.sequelizerc Normal file
View File

@@ -0,0 +1,8 @@
const path = require('path');
module.exports = {
"config": path.resolve('./src/db/config', 'config.json'),
"models-path": path.resolve('./src/db/models'),
"seeders-path": path.resolve('./src/db/seeders'),
"migrations-path": path.resolve('./src/db/migrations')
};

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "postgres",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"body-parser": "^1.18.3",
"dotenv": "^6.2.0",
"express": "^4.16.4",
"morgan": "^1.9.1",
"pg": "^7.8.0",
"pg-hstore": "^2.3.2"
},
"devDependencies": {
"sequelize": "^4.42.0",
"sequelize-auto": "^0.4.29",
"sequelize-cli": "^5.4.0"
}
}

14
server.js Normal file
View File

@@ -0,0 +1,14 @@
require('dotenv').config()
// console.log(process.env)
const { Client } = require('pg')
const client = new Client()
client.connect()
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => {
console.log(res.rows[0].message) // Yello world!
client.end()
})

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

16
src/app.js Normal file
View File

@@ -0,0 +1,16 @@
const express = require('express')
const logger = require('morgan')
const bodyParser = require('body-parser')
const app = express();
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
require('./routes')(app);
app.get('*', (req, res) => res.status(200).send({
message: 'Welcome to the beginning of nothingness',
}));
module.exports = app;

View File

@@ -0,0 +1,67 @@
const Adventure = require('../db/models').adventure;
const Location = require('../db/models').location;
const Image = require('../db/models').image;
// const { Adventure, Image, Location } = require('../db/sequelize')
const Op = require('sequelize').Op;
// module.exports = {
function location(req, res) {
return Adventure
.findAll({
where: {
'locationName': req.query.name
},
attributes: ['id', 'title', 'subtext', 'dateStart', 'dateEnd', 'locationName']
// order: [['title', 'ASC']]
})
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error))
}
function list(req, res) {
return Adventure
.findAll({
attributes: ['id', 'title', 'subtext', 'dateStart', 'dateEnd', 'locationName']
})
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error))
}
function get(req, res) {
return Adventure
.findByPk(req.params.id, {
attributes: ['id', 'title', 'subtext', 'dateStart', 'dateEnd', 'locationName']
})
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error));
}
function createLocation(req, res) {
return Location
.create({
name: req.body.locationName,
geoposition: req.body.geoposition
})
}
// get form elements
function create(req, res) {
console.log('adventure', Adventure)
return createLocation(req, res)
.then(() => Adventure
.create({
title: req.body.title,
subtext: req.body.subtext,
dateStart: req.body.dateStart,
dateEnd: req.body.dateEnd,
locationName: req.body.locationName,
}, {}))
.then(adventure => res.status(201).send(adventure))
.catch(error => res.status(400).send(error))
}
module.exports = { location, list, get, create };

View File

@@ -0,0 +1,47 @@
const Adventure = require('../db/models').adventure;
// const { Adventure } = require('../db/sequelize')
const Op = require('sequelize').Op;
module.exports = {
location(req, res) {
return Adventure
.findAll({
where: { [Op.not]: { locationName: null } }
})
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error))
},
list(req, res) {
return Adventure
.findAll({
attributes: ['id', 'title', 'subtext', 'dateStart', 'dateEnd', 'locationName']
})
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error))
},
get(req, res) {
return Adventure
.findById(req.params.id)
.then(adventure => res.status(200).send(adventure))
.catch(error => res.status(400).send(error));
},
// get form elements
create(req, res) {
console.log('adventure', Adventure)
return Adventure
.create({
title: req.body.title,
subtext: req.body.subtext,
dateStart: req.body.dateStart,
dateEnd: req.body.dateEnd,
locationName: req.body.locationName
})
.then(adventure => res.status(201).send(adventure))
.catch(error => res.status(400).send(error))
}
};

23
src/controllers/image.js Normal file
View File

@@ -0,0 +1,23 @@
const Image = require('../db/models').image;
// const { Image } = require('../db/sequelize')
module.exports = {
all(req, res) {
console.log('here')
return Image
.findAll()
.then(images => res.status(200).send(images))
.catch(error => res.status(400).send(error))
},
list(req, res) {
return Image
.findAll({
where: { 'adventure_id': req.params.adventureId }
})
.then(images => res.status(200).send(images))
.catch(error => res.status(400).send(error))
},
};

9
src/controllers/index.js Normal file
View File

@@ -0,0 +1,9 @@
const Adventure = require('./adventure');
const Location = require('./location');
const Image = require('./image');
module.exports = {
Adventure,
Location,
Image
}

View File

@@ -0,0 +1,22 @@
const Location = require('../db/models').location;
const Adventure = require('../db/models').adventure;
// const { Adventure, Location } = require('../db/sequelize')
const Op = require('sequelize').Op;
module.exports = {
list(req, res) {
return Location
.findAll({
attributes: ['name', 'geoposition', 'mapboxData']
})
// .then(Location => Adventure.findAll({
// where: { 'locationName': Location.name },
// attributes: [ 'title', 'subtext', 'dateStart', 'dateEnd', 'location.geoposition' ]
// }))
.then(Location => res.status(200).send(Location))
.catch(error => res.status(400).send(error))
},
};

BIN
src/db/.DS_Store vendored Normal file

Binary file not shown.

27
src/db/config/config.json Normal file
View File

@@ -0,0 +1,27 @@
{
"development": {
"username": "kevinmidboe",
"password": null,
"database": "postgres",
"host": "localhost",
"port": 5432,
"dialect": "postgres",
"operatorsAliases": false
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "postgres",
"operatorsAliases": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "postgres",
"operatorsAliases": false
}
}

View File

@@ -0,0 +1,61 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const Adventure = sequelize.define('adventure', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
// imageIds: {
// type: DataTypes.INTEGER,
// allowNull: false,
// references: {
// model: 'images',
// key: 'id',
// as: 'imageKey'
// }
// },
title: {
type: DataTypes.STRING,
notNull: true
},
subtext: {
type: DataTypes.STRING,
notNull: false,
default: null
},
dateStart: {
type: DataTypes.DATE,
notNull: true
},
dateEnd: {
type: DataTypes.DATE,
notNull: true
},
locationName: {
type: DataTypes.STRING,
notnull: false,
default: null,
// onDelete: 'SET NULL',
// references: {
// model: 'locations',
// key: 'name',
// as: 'locationKey',
// },
}
}, {});
Adventure.associate = (models) => {
Adventure.hasMany(models.image, {
foreignKey: 'adventure_id',
as: 'images'
});
// associations can be defined here
// Adventure.hasZeroOrOne(models.location)
};
// Adventure.sync({ force: false })
return Adventure;
};

51
src/db/models/image.js Normal file
View File

@@ -0,0 +1,51 @@
module.exports = (sequelize, DataTypes) => {
const Image = sequelize.define('image', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
filename: {
type: DataTypes.TEXT,
allowNull: false,
},
adventure_id: {
type: DataTypes.INTEGER,
},
// adventure_key: {
// type: DataTypes.INTEGER,
// allowNull: false,
// references: {
// model: 'adventures',
// key: 'id',
// as: 'adventureKey'
// }
// },
description: {
type: DataTypes.TEXT,
allowNull: true
},
size: {
type: DataTypes.DOUBLE,
allowNull: true
},
album_order: {
type: DataTypes.INTEGER,
allowNull: true
}
}, {});
Image.associate = (models) => {
Image.belongsTo(models.adventure, {
foreignKey: 'adventure_id'
});
Image.hasOne(models.imagevariations, {
foreignKey: 'image_id',
as: 'imageVariations'
})
}
// Image.sync({ force: false })
return Image;
};

View File

@@ -0,0 +1,44 @@
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
const ImageVariation = sequelize.define('imagevariation', {
'id': {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
image_id: {
type: DataTypes.INTEGER,
},
thumb: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: false
},
sm: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: false
},
md: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: false
},
lg: {
type: DataTypes.BOOLEAN,
allowNull: true,
defaultValue: false
}
}, {});
ImageVariation.associate = (models) => {
ImageVariation.belongsTo(models.iamge, {
foreignKey: 'image_id'
});
}
// ImageVariation.sync({ force: false })
return ImageVariation;
};

55
src/db/models/index.js Normal file
View File

@@ -0,0 +1,55 @@
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file =>
(file.indexOf('.') !== 0) &&
(file !== basename) &&
(file.slice(-3) === '.js'))
.forEach(file => {
console.log('file', file)
const model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
// const AdventureModel = require('./adventure')
// db.adventure = require('./adventure.js')(sequelize, Sequelize);
// db.image = require('./image.js')(sequelize, Sequelize);
// db.imagevariation = require('./imagevariation.js')(sequelize, Sequelize);
// db.location = require('./location.js')(sequelize, Sequelize);
// db.location.sync({ force: false });
// db.adventure.sync({ force: false });
// db.image.sync({ force: false });
// db.imagevariation.sync({ force: false });
// Object.keys(db).forEach(modelName => {
// console.log('modelName', modelName)
// if (db[modelName].associate) {
// console.log(`yes ${modelName} has an assocination`)
// console.log(db[modelName])
// console.log(db)
// db[modelName].associate(db);
// }
// });
db.sequelize = sequelize;
module.exports = db;

44
src/db/models/location.js Normal file
View File

@@ -0,0 +1,44 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const Location = sequelize.define('location', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
// primaryKey: true,
allowNull: false
},
geoposition: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null
},
mapboxData: {
type: DataTypes.JSON,
allowNull: true,
defaultValue: null
},
// adventureId: {
// type: DataTypes.INTEGER,
// onDelete: 'SET NULL',
// references: {
// model: 'adventures',
// key: 'id',
// as: 'adventureKey',
// },
// }
}, {});
Location.associate = function(models) {
// associations can be defined here
// Location.belongsTo(models.adventure)
};
// Location.sync({ force: false })
return Location;
};

View File

@@ -0,0 +1,36 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('adventures', [{
title: 'Jul 2019',
dateStart: '2019-12-24',
dateEnd: '2019-12-24',
locationName: 'Rosendalsveien 50b, 1166 Oslo, Norway',
createdAt: new Date(),
updatedAt: new Date()
},
{
title: 'Fisketur på Karmøy',
subtext: 'Flott tur rundt påsketider 🌞',
dateStart: '2019-04-12',
dateEnd: '2019-04-16',
locationName: 'Karmøy, Rogaland, Norge',
createdAt: new Date(),
updatedAt: new Date()
},
{
title: 'Havfiskeforeningen til karmøy!',
subtext: 'Vi er her for å herje! :D',
dateStart: '2019-05-01',
dateEnd: '2019-05-04',
locationName: 'Karmøy, Rogaland, Norge',
createdAt: new Date(),
updatedAt: new Date()
}], {});
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('adventures', null, {});
}
};

View File

@@ -0,0 +1,23 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('locations', [{
name: 'Rosendalsveien 50b, 1166 Oslo, Norway',
geoposition: '59.853973, 10.799471',
mapboxData: '{"id":"address.3598204582760676","type":"Feature","place_type":["address"],"relevance":1,"properties":{"accuracy":"point"},"text":"Rosendalsveien","place_name":"Rosendalsveien50b,1166Oslo,Norway","center":[10.799471,59.853973],"geometry":{"type":"Point","coordinates":[10.799471,59.853973]},"address":"50b","context":[{"id":"postcode.9489910510813950","text":"1166"},{"id":"place.17289044417596980","short_code":"NO-03","wikidata":"Q585","text":"Oslo"},{"id":"country.16020050790143780","short_code":"no","wikidata":"Q20","text":"Norway"}]}',
createdAt: new Date(),
updatedAt: new Date()
},
{
name: 'Karmøy, Rogaland, Norge',
geoposition: '59.2744, 5.2884',
createdAt: new Date(),
updatedAt: new Date()
}], {});
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('locations', null, {});
}
};

View File

@@ -0,0 +1,52 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('images', [{
filename: 'DSC_9029.png',
adventure_id: 1,
size: 2983546,
createdAt: new Date(),
updatedAt: new Date()
},
{
filename: 'DSC_0505.png',
adventure_id: 1,
description: 'Dancing around the christmas tree',
size: 10032272,
album_order: 1,
createdAt: new Date(),
updatedAt: new Date()
},
{
filename: 'DSC_0509.png',
adventure_id: 2,
description: 'Se på den fisken der!',
size: 8032272,
album_order: 2,
createdAt: new Date(),
updatedAt: new Date()
},
{
filename: 'DSC_0510.png',
adventure_id: 2,
description: 'Noe i garnet!',
size: 6373234,
album_order: 1,
createdAt: new Date(),
updatedAt: new Date()
}], {})
// const adventure = await queryInterface.Sequelize.query(
// `SELECT id FROM adventure;`
// );
// const adventure = images[0];
// return await queryInterface.bulkInsert('images')
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('images', null, {});
}
};

View File

@@ -0,0 +1,18 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('imagevariations', [{
'image_id': 4,
'thumb': false,
'md': true,
'lg': true,
createdAt: new Date(),
updatedAt: new Date()
}])
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('imagevariations', null, {});
}
};

39
src/db/sequelize.js Normal file
View File

@@ -0,0 +1,39 @@
const Sequelize = require('sequelize')
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/config/config.json')[env];
const AdventureModel = require('./models/adventure')
const ImageModel = require('./models/image')
const ImageVariationModel = require('./models/imagevariation')
const LocationModel = require('./models/location')
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
const Adventure = AdventureModel(sequelize, Sequelize)
// BlogTag will be our way of tracking relationship between Blog and Tag models
// each Blog can have multiple tags and each Tag can have multiple blogs
const AdventureImages = sequelize.define('adventure_image', {})
const Image = ImageModel(sequelize, Sequelize)
const ImageVariation = ImageVariationModel(sequelize, Sequelize)
const Location = LocationModel(sequelize, Sequelize)
// Adventure.hasMany(Image, { through: AdventureImages, unique: false })
// Image.belongsTo(Adventure, { through: AdventureImages, unique: false })
// Blog.belongsTo(User);
sequelize.sync({ force: false })
.then(() => {
console.log(`Database & tables created!`)
})
module.exports = {
Adventure,
Image,
ImageVariation,
Location
}

23
src/routes/index.js Normal file
View File

@@ -0,0 +1,23 @@
const adventureController = require('../controllers').Adventure;
const locationController = require('../controllers').Location;
const imageController = require('../controllers').Image;
module.exports = (app) => {
app.get('/api', (req, res) => res.status(200).send({
message: 'Welcome to Leifs Adventures!'
}));
// Adventures
app.get('/api/adventure', adventureController.list)
app.post('/api/adventure', adventureController.create)
app.get('/api/adventure/location', adventureController.location);
app.get('/api/adventure/:id', adventureController.get);
// Location (map)
app.get('/api/location', locationController.list)
// images
app.get('/api/images', imageController.all)
app.get('/api/images/:adventureId', imageController.list)
}

8
src/server.js Normal file
View File

@@ -0,0 +1,8 @@
const app = require('./app');
const port = parseInt(process.env.PORT) || 5000;
module.exports = app.listen(port, () => {
console.log('leifsBackend');
console.log(`Webserver is listening on ${port}`);
});

2368
yarn.lock Normal file

File diff suppressed because it is too large Load Diff