diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/client/Makefile b/client/Makefile
new file mode 100644
index 0000000..5a8a531
--- /dev/null
+++ b/client/Makefile
@@ -0,0 +1,9 @@
+.PHONY: dist build
+install:
+ @npm install
+
+dev: install
+ @npm run dev
+
+build:
+ @npm run build
diff --git a/client/README.md b/client/README.md
new file mode 100644
index 0000000..cb832b7
--- /dev/null
+++ b/client/README.md
@@ -0,0 +1,32 @@
+# element-starter
+
+> A starter kit for Element UI generated by vue-cli
+
+*If you are familiar with [cooking](https://github.com/elemefe/cooking), [here](https://github.com/ElementUI/element-cooking-starter) is a starter generated with it*
+
+## Environment
+
+`Node >= 6`
+
+## Start
+
+ - Clone or download this repository
+ - Enter your local directory, and install dependencies:
+
+``` bash
+npm install
+```
+
+## Develop
+
+``` bash
+# serve with hot reload at localhost:8010
+npm run dev
+```
+
+## Build
+
+``` bash
+# build for production with minification
+npm run build
+```
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..b35ce9f
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "element-starter",
+ "description": "A Vue.js project",
+ "author": "yi.shyang@ele.me",
+ "private": true,
+ "scripts": {
+ "dev": "webpack-dev-server -d --inline --hot --env.dev",
+ "build": "rimraf dist && webpack -p --progress --hide-modules"
+ },
+ "dependencies": {
+ "axios": "^0.15.3",
+ "element-ui": "^1.1.2",
+ "lodash": "^4.17.4",
+ "rupiah-format": "^1.0.0",
+ "slug": "^0.9.1",
+ "vue": "^2.1.8",
+ "vue-localstorage": "^0.1.3",
+ "vue-router": "^2.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "devDependencies": {
+ "autoprefixer": "^6.6.0",
+ "babel-core": "^6.21.0",
+ "babel-eslint": "^7.1.1",
+ "babel-loader": "^6.2.10",
+ "babel-preset-es2015": "^6.13.2",
+ "css-loader": "^0.26.1",
+ "element-theme": "^0.7.1",
+ "eslint": "^3.12.2",
+ "eslint-config-enough": "^0.2.2",
+ "eslint-loader": "^1.6.1",
+ "file-loader": "^0.9.0",
+ "html-loader": "^0.4.4",
+ "html-webpack-plugin": "^2.24.1",
+ "postcss-loader": "^1.2.1",
+ "rimraf": "^2.5.4",
+ "style-loader": "^0.13.1",
+ "url-loader": "^0.5.7",
+ "vue-loader": "^10.0.0",
+ "vue-template-compiler": "^2.1.8",
+ "webpack": "^2.2.0-rc.4",
+ "webpack-dev-server": "beta"
+ }
+}
diff --git a/client/postcss.config.js b/client/postcss.config.js
new file mode 100644
index 0000000..1b602ce
--- /dev/null
+++ b/client/postcss.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ plugins: [
+ require('autoprefixer')()
+ ]
+}
diff --git a/client/src/App.vue b/client/src/App.vue
new file mode 100644
index 0000000..a92de63
--- /dev/null
+++ b/client/src/App.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/assets/logo.png b/client/src/assets/logo.png
new file mode 100644
index 0000000..f3d2503
Binary files /dev/null and b/client/src/assets/logo.png differ
diff --git a/client/src/components/Home.vue b/client/src/components/Home.vue
new file mode 100644
index 0000000..d138b90
--- /dev/null
+++ b/client/src/components/Home.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/Shop.vue b/client/src/components/Shop.vue
new file mode 100644
index 0000000..58dd35a
--- /dev/null
+++ b/client/src/components/Shop.vue
@@ -0,0 +1,156 @@
+
+
+
Checkout
+
+
+
+
+
+
+
+
+
+
+
{{ item.name }}
+
+
+ {{ item.description }}
+
+
{{toRupiah(item.price )}}
+
Add to cart
+
+
+
+
+
+
+
+
+
+ Shoping cart
+ Purchase now
+
+
+ {{ item.product_name }} [ X {{ item.quantity }}
+ ]{{ toRupiah(item.subtotal) }}
+
+
+
+
+ Total {{ total }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/item/Item.vue b/client/src/components/item/Item.vue
new file mode 100644
index 0000000..d138b90
--- /dev/null
+++ b/client/src/components/item/Item.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/item/ItemAll.vue b/client/src/components/item/ItemAll.vue
new file mode 100644
index 0000000..315533a
--- /dev/null
+++ b/client/src/components/item/ItemAll.vue
@@ -0,0 +1,87 @@
+
+
+ Add item
+
+
+
+
+
+
+
+
+
+
+
+ Edit
+ Delete
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/item/ItemDetail.vue b/client/src/components/item/ItemDetail.vue
new file mode 100644
index 0000000..b4856ea
--- /dev/null
+++ b/client/src/components/item/ItemDetail.vue
@@ -0,0 +1,22 @@
+
+
+ back to home
+
+
+
Detail Item
+ {{ item }}
+
+
+
diff --git a/client/src/components/item/ItemEdit.vue b/client/src/components/item/ItemEdit.vue
new file mode 100644
index 0000000..ab8a852
--- /dev/null
+++ b/client/src/components/item/ItemEdit.vue
@@ -0,0 +1,76 @@
+
+
+ back to list
+
+
+
Edit an item
+
+
+
+
+
+
+ Rp.
+
+
+
+
+
+ Http://
+
+
+
+
+
+
+
+
+ Update item
+ Cancel
+
+
+
+
+
+
diff --git a/client/src/components/item/ItemNew.vue b/client/src/components/item/ItemNew.vue
new file mode 100644
index 0000000..e57eeee
--- /dev/null
+++ b/client/src/components/item/ItemNew.vue
@@ -0,0 +1,66 @@
+
+
+ back to list
+
+
+
Add new item
+
+
+
+
+
+
+ Rp.
+
+
+
+
+
+ Http://
+
+
+
+
+
+
+
+
+ Add item
+ Cancel
+
+
+
+
+
+
diff --git a/client/src/components/post/Post.vue b/client/src/components/post/Post.vue
new file mode 100644
index 0000000..d138b90
--- /dev/null
+++ b/client/src/components/post/Post.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/post/PostAll.vue b/client/src/components/post/PostAll.vue
new file mode 100644
index 0000000..a9881ec
--- /dev/null
+++ b/client/src/components/post/PostAll.vue
@@ -0,0 +1,90 @@
+
+
+ Create post
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Edit
+ Delete
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/post/PostDetail.vue b/client/src/components/post/PostDetail.vue
new file mode 100644
index 0000000..b4856ea
--- /dev/null
+++ b/client/src/components/post/PostDetail.vue
@@ -0,0 +1,22 @@
+
+
+ back to home
+
+
+
Detail Item
+ {{ item }}
+
+
+
diff --git a/client/src/components/post/PostEdit.vue b/client/src/components/post/PostEdit.vue
new file mode 100644
index 0000000..ce3cf58
--- /dev/null
+++ b/client/src/components/post/PostEdit.vue
@@ -0,0 +1,75 @@
+
+
+ back to list
+
+
+
Edit an item
+
+
+
+
+
+
+
+
+
+
+ Http://dikyarga.com/
+
+
+
+
+
+
+
+
+
+ Update blogpost
+ Cancel
+
+
+
+
+
+
diff --git a/client/src/components/post/PostNew.vue b/client/src/components/post/PostNew.vue
new file mode 100644
index 0000000..cc3f616
--- /dev/null
+++ b/client/src/components/post/PostNew.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+ back to list
+
+
+
Add new blogpost
+
+
+
+
+
+
+
+
+
+
+ Http://dikyarga.com/
+
+
+
+
+
+
+
+
+
+ Posting blogpost
+ Cancel
+
+
+
+
+
+
+
diff --git a/client/src/components/user/User.vue b/client/src/components/user/User.vue
new file mode 100644
index 0000000..4729d6b
--- /dev/null
+++ b/client/src/components/user/User.vue
@@ -0,0 +1,11 @@
+
+ This is USERS page
+
+
+
+
+
diff --git a/client/src/components/user/UserDetail.vue b/client/src/components/user/UserDetail.vue
new file mode 100644
index 0000000..eab90d2
--- /dev/null
+++ b/client/src/components/user/UserDetail.vue
@@ -0,0 +1,11 @@
+
+ This is user detail page
+
+
+
+
+
diff --git a/client/src/index.html b/client/src/index.html
new file mode 100644
index 0000000..21b90e8
--- /dev/null
+++ b/client/src/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Diky Arga Vue Blog
+
+
+
+
+
+
+
+
diff --git a/client/src/main.js b/client/src/main.js
new file mode 100644
index 0000000..7aa5ecf
--- /dev/null
+++ b/client/src/main.js
@@ -0,0 +1,23 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-default/index.css'
+import VueLocalStorage from 'vue-localstorage'
+import App from './App.vue'
+
+
+import { routes } from './routes'
+
+Vue.use(VueRouter)
+Vue.use(ElementUI)
+Vue.use(VueLocalStorage)
+const router = new VueRouter({
+ routes,
+ mode: 'history'
+})
+
+new Vue({
+ el: '#app',
+ router,
+ render: h => h(App)
+})
diff --git a/client/src/routes.js b/client/src/routes.js
new file mode 100644
index 0000000..e894d89
--- /dev/null
+++ b/client/src/routes.js
@@ -0,0 +1,36 @@
+import Home from './components/Home.vue'
+import Shop from './components/Shop.vue'
+import User from './components/user/User.vue'
+
+import Item from './components/item/Item.vue'
+import ItemAll from './components/item/ItemAll.vue'
+import ItemDetail from './components/item/ItemDetail.vue'
+import ItemNew from './components/item/ItemNew.vue'
+import ItemEdit from './components/item/ItemEdit.vue'
+
+import Post from './components/post/Post.vue'
+import PostAll from './components/post/PostAll.vue'
+import PostDetail from './components/post/PostDetail.vue'
+import PostNew from './components/post/PostNew.vue'
+import PostEdit from './components/post/PostEdit.vue'
+
+export const routes = [
+ { path : '', component: Home, children: [
+ {path: '/shop', component: Shop}
+ ]},
+ { path : '/users', component: User},
+ { path : '/items', component:Item, children: [
+ {path: '', component: ItemAll},
+ {path: 'new', component: ItemNew},
+ {path: ':id', component: ItemDetail},
+ {path: ':id/edit', component: ItemEdit}
+ ]},
+ {
+ path: '/posts', component: Post, children: [
+ {path: '', component: PostAll},
+ {path: 'new', component: PostNew},
+ {path: ':id', component: PostDetail},
+ {path: ':id/edit', component: PostEdit}
+ ]
+ }
+]
diff --git a/client/src/vendor.js b/client/src/vendor.js
new file mode 100644
index 0000000..986e5d0
--- /dev/null
+++ b/client/src/vendor.js
@@ -0,0 +1,2 @@
+import Vue from 'vue'
+import ElementUI from 'element-ui'
\ No newline at end of file
diff --git a/client/webpack.config.js b/client/webpack.config.js
new file mode 100644
index 0000000..b617dee
--- /dev/null
+++ b/client/webpack.config.js
@@ -0,0 +1,95 @@
+const {
+ resolve
+} = require('path')
+const webpack = require('webpack')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const url = require('url')
+const publicPath = ''
+
+module.exports = (options = {}) => ({
+ entry: {
+ vendor: './src/vendor',
+ index: './src/main.js'
+ },
+ output: {
+ path: resolve(__dirname, 'dist'),
+ filename: options.dev ? '[name].js' : '[name].js?[chunkhash]',
+ chunkFilename: '[id].js?[chunkhash]',
+ publicPath: options.dev ? '/assets/' : publicPath
+ },
+ module: {
+ rules: [{
+ test: /\.vue$/,
+ use: ['vue-loader']
+ },
+ {
+ test: /\.js$/,
+ use: ['babel-loader'],
+ exclude: /node_modules/
+ },
+ {
+ test: /\.html$/,
+ use: [{
+ loader: 'html-loader',
+ options: {
+ root: resolve(__dirname, 'src'),
+ attrs: ['img:src', 'link:href']
+ }
+ }]
+ },
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader', 'postcss-loader']
+ },
+ {
+ test: /favicon\.png$/,
+ use: [{
+ loader: 'file-loader',
+ options: {
+ name: '[name].[ext]?[hash]'
+ }
+ }]
+ },
+ {
+ test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
+ exclude: /favicon\.png$/,
+ use: [{
+ loader: 'url-loader',
+ options: {
+ limit: 10000
+ }
+ }]
+ }
+ ]
+ },
+ plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ names: ['vendor', 'manifest']
+ }),
+ new HtmlWebpackPlugin({
+ template: 'src/index.html'
+ })
+ ],
+ resolve: {
+ alias: {
+ '~': resolve(__dirname, 'src')
+ }
+ },
+ devServer: {
+ host: '127.0.0.1',
+ port: 8010,
+ proxy: {
+ '/api/': {
+ target: 'http://127.0.0.1:8080',
+ changeOrigin: true,
+ pathRewrite: {
+ '^/api': ''
+ }
+ }
+ },
+ historyApiFallback: {
+ index: url.parse(options.dev ? '/assets/' : publicPath).pathname
+ }
+ },
+ devtool: options.dev ? '#eval-source-map' : '#source-map'
+})
\ No newline at end of file
diff --git a/README.md b/server/README.md
similarity index 100%
rename from README.md
rename to server/README.md
diff --git a/server/app.js b/server/app.js
new file mode 100644
index 0000000..9cc384d
--- /dev/null
+++ b/server/app.js
@@ -0,0 +1,49 @@
+var express = require('express');
+var cors = require('cors')
+var path = require('path');
+var favicon = require('serve-favicon');
+var logger = require('morgan');
+var cookieParser = require('cookie-parser');
+var bodyParser = require('body-parser');
+
+var index = require('./routes/index');
+var users = require('./routes/users');
+var postsApi = require('./routes/apis/posts');
+
+var app = express();
+app.use(cors())
+// 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(path.join(__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('/', index);
+app.use('/users', users);
+app.use('/api/posts', postsApi);
+
+// 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 handler
+app.use(function(err, req, res, next) {
+ // set locals, only providing error in development
+ res.locals.message = err.message;
+ res.locals.error = req.app.get('env') === 'development' ? err : {};
+
+ // render the error page
+ res.status(err.status || 500);
+ res.render('error');
+});
+
+module.exports = app;
diff --git a/server/bin/www b/server/bin/www
new file mode 100755
index 0000000..aa1db4f
--- /dev/null
+++ b/server/bin/www
@@ -0,0 +1,90 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+var app = require('../app');
+var debug = require('debug')('blog-tdd:server');
+var http = require('http');
+
+/**
+ * Get port from environment and store in Express.
+ */
+
+var port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+/**
+ * Create HTTP server.
+ */
+
+var server = http.createServer(app);
+
+/**
+ * Listen on provided port, on all network interfaces.
+ */
+
+server.listen(port);
+server.on('error', onError);
+server.on('listening', onListening);
+
+/**
+ * Normalize a port into a number, string, or false.
+ */
+
+function normalizePort(val) {
+ var port = parseInt(val, 10);
+
+ if (isNaN(port)) {
+ // named pipe
+ return val;
+ }
+
+ if (port >= 0) {
+ // port number
+ return port;
+ }
+
+ return false;
+}
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+
+function onError(error) {
+ if (error.syscall !== 'listen') {
+ throw error;
+ }
+
+ var bind = typeof port === 'string'
+ ? 'Pipe ' + port
+ : 'Port ' + port;
+
+ // handle specific listen errors with friendly messages
+ switch (error.code) {
+ case 'EACCES':
+ console.error(bind + ' requires elevated privileges');
+ process.exit(1);
+ break;
+ case 'EADDRINUSE':
+ console.error(bind + ' is already in use');
+ process.exit(1);
+ break;
+ default:
+ throw error;
+ }
+}
+
+/**
+ * Event listener for HTTP server "listening" event.
+ */
+
+function onListening() {
+ var addr = server.address();
+ var bind = typeof addr === 'string'
+ ? 'pipe ' + addr
+ : 'port ' + addr.port;
+ debug('Listening on ' + bind);
+}
diff --git a/server/config/config.json b/server/config/config.json
new file mode 100644
index 0000000..8185ec0
--- /dev/null
+++ b/server/config/config.json
@@ -0,0 +1,23 @@
+{
+ "development": {
+ "username": "dikyarga",
+ "password": "dikyarga",
+ "database": "blog",
+ "host": "127.0.0.1",
+ "dialect": "postgres"
+ },
+ "test": {
+ "username": "root",
+ "password": null,
+ "database": "database_test",
+ "host": "127.0.0.1",
+ "dialect": "mysql"
+ },
+ "production": {
+ "username": "root",
+ "password": null,
+ "database": "database_production",
+ "host": "127.0.0.1",
+ "dialect": "mysql"
+ }
+}
diff --git a/server/controllers/postController.js b/server/controllers/postController.js
new file mode 100644
index 0000000..441fed6
--- /dev/null
+++ b/server/controllers/postController.js
@@ -0,0 +1,61 @@
+let db = require('../models')
+
+let slug = require('slug')
+
+module.exports = {
+ index: function(req, res, next){
+ db.Post.findAll().then(function(posts){
+ res.json(posts)
+ })
+ },
+ create: function(req, res, next){
+ let published = req.body.published == 'true' || true
+ db.Post.create({
+ title: req.body.title,
+ body: req.body.body,
+ slug: slug(req.body.title, {lower: true}),
+ published: published
+ }).then(() => {
+ res.json({
+ status: true,
+ msg: 'Post saved'
+ })
+ })
+ },
+ show: function(req, res, next){
+ db.Post.findById(req.params.id).then((post) => {
+ res.json(post)
+ })
+ },
+ update: function(req, res, next){
+ let published = req.body.published == 'true' || true
+
+ db.Post.update({
+ title: req.body.title,
+ body: req.body.body,
+ slug: slug(req.body.title, {lower: true}),
+ published: published
+ }, {
+ where: {
+ id: req.params.id
+ }
+ }).then(() => {
+ res.json({
+ status: true,
+ msg: 'Post updated'
+ })
+ })
+ },
+ destroy: function(req, res, next){
+ db.Post.destroy({
+ where: {
+ id: req.params.id
+ }
+ }).then(() => {
+ res.json({
+ status: true,
+ msg: 'Post destroyed'
+ })
+ })
+ }
+}
diff --git a/server/migrations/20170313090315-create-post.js b/server/migrations/20170313090315-create-post.js
new file mode 100644
index 0000000..3475c26
--- /dev/null
+++ b/server/migrations/20170313090315-create-post.js
@@ -0,0 +1,36 @@
+'use strict';
+module.exports = {
+ up: function(queryInterface, Sequelize) {
+ return queryInterface.createTable('Posts', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ title: {
+ type: Sequelize.STRING
+ },
+ body: {
+ type: Sequelize.STRING
+ },
+ slug: {
+ type: Sequelize.STRING
+ },
+ published: {
+ type: Sequelize.BOOLEAN
+ },
+ createdAt: {
+ allowNull: false,
+ type: Sequelize.DATE
+ },
+ updatedAt: {
+ allowNull: false,
+ type: Sequelize.DATE
+ }
+ });
+ },
+ down: function(queryInterface, Sequelize) {
+ return queryInterface.dropTable('Posts');
+ }
+};
\ No newline at end of file
diff --git a/server/models/index.js b/server/models/index.js
new file mode 100644
index 0000000..7540dba
--- /dev/null
+++ b/server/models/index.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+var Sequelize = require('sequelize');
+var basename = path.basename(module.filename);
+var env = process.env.NODE_ENV || 'development';
+var config = require(__dirname + '/../config/config.json')[env];
+var db = {};
+
+if (config.use_env_variable) {
+ var sequelize = new Sequelize(process.env[config.use_env_variable]);
+} else {
+ var sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(function(file) {
+ return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
+ })
+ .forEach(function(file) {
+ var model = sequelize['import'](path.join(__dirname, file));
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(function(modelName) {
+ if (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
diff --git a/server/models/post.js b/server/models/post.js
new file mode 100644
index 0000000..d884408
--- /dev/null
+++ b/server/models/post.js
@@ -0,0 +1,16 @@
+'use strict';
+module.exports = function(sequelize, DataTypes) {
+ var Post = sequelize.define('Post', {
+ title: DataTypes.STRING,
+ body: DataTypes.STRING,
+ slug: DataTypes.STRING,
+ published: DataTypes.BOOLEAN
+ }, {
+ classMethods: {
+ associate: function(models) {
+ // associations can be defined here
+ }
+ }
+ });
+ return Post;
+};
\ No newline at end of file
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..81d476f
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "blog-tdd",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "start": "node ./bin/www",
+ "dev": "nodemon ./bin/www"
+ },
+ "dependencies": {
+ "body-parser": "~1.16.0",
+ "cookie-parser": "~1.4.3",
+ "cors": "^2.8.1",
+ "debug": "~2.6.0",
+ "express": "~4.14.1",
+ "jade": "~1.11.0",
+ "morgan": "~1.7.0",
+ "pg": "^6.1.4",
+ "sequelize": "^3.30.2",
+ "serve-favicon": "~2.3.2",
+ "slug": "^0.9.1"
+ }
+}
diff --git a/server/public/stylesheets/style.css b/server/public/stylesheets/style.css
new file mode 100644
index 0000000..9453385
--- /dev/null
+++ b/server/public/stylesheets/style.css
@@ -0,0 +1,8 @@
+body {
+ padding: 50px;
+ font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+a {
+ color: #00B7FF;
+}
diff --git a/server/routes/apis/posts.js b/server/routes/apis/posts.js
new file mode 100644
index 0000000..992e2b6
--- /dev/null
+++ b/server/routes/apis/posts.js
@@ -0,0 +1,17 @@
+var express = require('express');
+var router = express.Router();
+
+var postController = require('../../controllers/postController')
+
+/* GET users listing. */
+router.get('/', postController.index);
+
+router.get('/:id', postController.show);
+
+router.post('/', postController.create)
+
+router.put('/:id', postController.update);
+
+router.delete('/:id', postController.destroy);
+
+module.exports = router;
diff --git a/server/routes/index.js b/server/routes/index.js
new file mode 100644
index 0000000..418f4f5
--- /dev/null
+++ b/server/routes/index.js
@@ -0,0 +1,13 @@
+var express = require('express');
+var router = express.Router();
+
+/* GET home page. */
+router.get('/', function(req, res, next) {
+ res.render('index', { title: 'Express' });
+});
+
+router.get('/ping', function(req, res, next){
+ res.json({msg:'pong'})
+})
+
+module.exports = router;
diff --git a/server/routes/posts.js b/server/routes/posts.js
new file mode 100644
index 0000000..c55a9c3
--- /dev/null
+++ b/server/routes/posts.js
@@ -0,0 +1,15 @@
+var express = require('express');
+var router = express.Router();
+
+var postController = require('../controllers/postController')
+
+/* GET users listing. */
+router.get('/', function(req, res, next) {
+ res.send('respond with a resource');
+});
+
+router.post('/', function(req, res, next){
+
+})
+
+module.exports = router;
diff --git a/server/routes/users.js b/server/routes/users.js
new file mode 100644
index 0000000..623e430
--- /dev/null
+++ b/server/routes/users.js
@@ -0,0 +1,9 @@
+var express = require('express');
+var router = express.Router();
+
+/* GET users listing. */
+router.get('/', function(req, res, next) {
+ res.send('respond with a resource');
+});
+
+module.exports = router;
diff --git a/server/test/blog-test.js b/server/test/blog-test.js
new file mode 100644
index 0000000..dfd65d4
--- /dev/null
+++ b/server/test/blog-test.js
@@ -0,0 +1,117 @@
+var chai = require('chai')
+var chaiHttp = require('chai-http')
+
+var should = chai.should()
+
+chai.use(chaiHttp)
+
+let serverHost = 'http://localhost:3000'
+
+
+describe('Server is running test', () => {
+ it('Should be return "pong" when try to access /ping', (done) => {
+ chai.request(serverHost).get('/ping').end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ // res.body.should.be.json;
+ res.body.msg.should.equal('pong');
+ done()
+ }
+ });
+ })
+})
+
+describe('CRUD Post test', () => {
+ describe('Read all data', () => {
+ it('Should be return array of post when try to access "/api.posts/"', (done) => {
+ chai.request(serverHost).get('/api/posts').end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.should.be.a('array');
+ res.body[0].should.have.property('title');
+ res.body[0].should.have.property('body');
+ res.body[0].should.have.property('slug');
+ done()
+ }
+ });
+ })
+ })
+ describe('Create a post', () => {
+ it('Should be return success message when try to create post "/api/posts"', (done) => {
+ chai.request(serverHost)
+ .post('/api/posts')
+ .send({
+ title: 'Test from Chai',
+ body: 'Ini body test bukan bodyParser',
+ published: true
+ })
+ .end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.status.should.equal(true)
+ done()
+ }
+ });
+ })
+ })
+ describe('Retrive single post', () => {
+ it('Should be return a post data when try to get single post "/api/posts/1"', (done) => {
+ chai.request(serverHost).get('/api/posts/1').end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.should.be.an('object')
+ res.body.should.have.property('title');
+ res.body.should.have.property('body');
+ res.body.should.have.property('slug');
+ done()
+ }
+ });
+ })
+ })
+ describe('Update a post', () => {
+ it('Should be return success message when try to Update blogpost "/api/posts/1"', (done) => {
+ chai.request(serverHost)
+ .put('/api/posts/1')
+ .send({
+ title: 'Test from Chai Updated',
+ body: 'Ini body test bukan bodyParser Updated',
+ published: true
+ })
+ .end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.status.should.equal(true)
+ done()
+ }
+ });
+ })
+ })
+ describe('Detele single post', () => {
+ it('Should be return success message when try to delete single blogpost "/api/posts/2"', (done) => {
+ chai.request(serverHost).delete('/api/posts/2').end((err, res) => {
+ if (err) {
+ done(err)
+ } else {
+ res.should.have.status(200);
+ res.should.be.json;
+ res.body.should.be.an('object')
+ res.body.status.should.equal(true)
+ done()
+ }
+ });
+ })
+ })
+})
diff --git a/server/views/error.jade b/server/views/error.jade
new file mode 100644
index 0000000..51ec12c
--- /dev/null
+++ b/server/views/error.jade
@@ -0,0 +1,6 @@
+extends layout
+
+block content
+ h1= message
+ h2= error.status
+ pre #{error.stack}
diff --git a/server/views/index.jade b/server/views/index.jade
new file mode 100644
index 0000000..3d63b9a
--- /dev/null
+++ b/server/views/index.jade
@@ -0,0 +1,5 @@
+extends layout
+
+block content
+ h1= title
+ p Welcome to #{title}
diff --git a/server/views/layout.jade b/server/views/layout.jade
new file mode 100644
index 0000000..15af079
--- /dev/null
+++ b/server/views/layout.jade
@@ -0,0 +1,7 @@
+doctype html
+html
+ head
+ title= title
+ link(rel='stylesheet', href='/stylesheets/style.css')
+ body
+ block content
diff --git a/untitled.webm b/untitled.webm
new file mode 100644
index 0000000..16e6645
Binary files /dev/null and b/untitled.webm differ