Программно-аппаратное обеспечение сервера и клиента

Запуск разработанного Web-приложения и надёжность его работы без сбоев должно обеспечивать аппаратное обеспечение. Любая компьютерная программа для нормальной работы должна располагать определенным количеством ресурсов и, если таких ресурсов по каким-либо причинам не хватает, программа может потерять часть своей функциональности, либо при критической нехватке ресурсов полностью утратить работоспособность.

Управление Web-приложением осуществляется посредством навигационного меню, поэтому для полноценного функционирования программного товара необходимо наличие манипулятора мышь.

Физическое размещение системы в целом и расположение её отдельных подсистем представлено на диаграмме размещения в соответствии с рисунком 6.3.1.

Рисунок 6.3.1 - Диаграмма размещения

Технические характеристики компьютера, на котором проводилось тестирование:

· процессор Intel Core i5-2500 3.3 GHz;

· 4,00 ГБ оперативной памяти, DDR3;

· видеокарта AMD ATI HD6470M.

Приветствуется изменение указанных характеристик в сторону увеличения. Изменение характеристик в сторону уменьшения не желательно, поскольку это приведет к значительному снижению работоспособности Web-приложения. Для правильности работы сайта, рекомендуется использовать браузер Google Chrome версии 31 и выше.

Руководство пользователя

Для работы с Web-приложением необходим доступ к сети интернет.

Для работы с интернет-магазином необходимо ввести в адресную строку адрес моего интернет-магазина и нажать на Enter. После этого загрузится главная страница сайта в соответствии с рисунком 6.4.1.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.1 – Главная страница.

Для регистрации необходимо нажать на кнопку «Авторизация». После этого откроется страница «Авторизация» соответствии с рисунком 6.4.2.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.2 – страница «Авторизация».

На этой странице необходимо нажать кнопку «Регистрация», появится форма для регистрации, в которой необходимо заполнить все поля и нажать кнопку «Зарегистрироваться». Все поля являются обязательными для заполнения. Если поля оставить не заполненными, то отобразится ошибка в соответствии с рисунком 6.4.3.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.3 – Сообщение об ошибке.

Если введенный логин совпадет с уже зарегистрировавшимся пользователем, то на экране отобразится сообщение об ошибке в соответствии с рисунком 6.4.4.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.4 – Сообщение об ошибке.

После правильного ввода всех необходимых данных откроется страница «Каталог» в соответствии с рисунком 6.4.5.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.5 – страница «Каталог».

Для того что бы заказать товар необходимо выбрать категорию. Откроется страница со списком товаров в категории, в соответствии с рисунком 6.4.6.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.6 – Страница категории «Товары для котов».

На этой странице под необходимым товаром в поле необходимо указать нужное число единиц товара и нажать кнопку «Добавить в корзину». Затем с помощью главного меню перейти на страницу «Корзина», в соответствии с рисунком 6.4.7.

Программно-аппаратное обеспечение сервера и клиента - student2.ru

Рисунок 6.4.7 – Страница «Корзина».

На этой странице необходимо выбирать ваш регион и ввести адрес. В зависимости от стоимости заказа и выбранного региона будет выведена информация о стоимости доставки товаров. Если пользователя все устраивает необходимо нажать на кнопку «Подтвердить заказ».

Заключение

В процессе проектирования курсового проекта был разработан интернет-магазин по продажам товаров для домашних животных.

Интернет-магазин предназначен для продажи различных товаров через интернет. Областью применения данного Web-приложения выступает интернет-торговля.

Создание данного интернет-магазина позволит продвигать и продавать товары, уменьшить издержки на рекламу и приобрести дополнительный рынок сбыта своего товара, расширить клиентскую аудиторию и, как следствие, увеличение прибыли.

Интернет-магазин имеет удобный и понятный интерфейс, благодаря которому не требуется излишних усилий в освоении работы с сайтом. Вся информация не содержит ничего лишнего.

Результаты проверки показали, что система работает без ошибок, имеется возможность работы с веб-сайтом во всех популярных браузерах.

В результате разработки курсового проекта были отточены навыки в дизайне сайтов, проектировки интерфейсов, разработке логотипов. Приобретены практические умения JavaScript, HTML и CSS верстки сайтов.




Список литературы

1. Node.js Разработка серверных веб-приложений на JavaScript/ Дэвид Хэррон Изд-во: ДМК Пресс, 2012 – 146 с.

2. Изучаем JavaScript // Майкл Моррисон — СПб.: Питер, 2012. — С.608

3. Титтел Эд, Бурмейстер Мэри. HTML 4 для "чайников". 6-е издание. М.: Из-дательский дом “Диалектика -Вильямс” – 2009. - 368с.

4. Веб-приложения на JavaScript. Год: 2012. // Маккоу — СПб.: Питер, 2012. — С.560 608.

5. Большая книга CSS3. // Дэвид Макфарланд. — СПб.: Питер, 2014. — С. 608.

6. HTML5 и CSS3. Веб-разработка по стандартам нового поколения // Брайан Хоган — СПб.: Питер, 2014. — С. 272.

7. Лебедев А. Руководство Лебедев А. – Москва.: Издательство Студии Артемия Лебедева, 2011. – 452 с.

8. Спейнауэр С. Справочник Web-мастера. СПб.: BHV, 1997. – 368 с.

9. Ратшиллер Т., Геркен Т. PHP4: разработка Web-приложений. - СПб: Питер, 2001. - 384 с.

10. Томсон Л., Веллинг Л. Разработка Web-приложений на PHP и MySQL. - К.: "ДиаСофт", 2001. - 672 с

11. Яргер Р., Риз Дж., Кинг Т. MySQL и mSQL. Базы данных для небольших предприятий и Интернета. - СПб: Символ-Плюс, 2000 - 560 с.

12. Орлов С.Д. Технологии разработки программного обеспечения. Учеб. пособие. 2-е изд. СПб.: Питер, 2003. – 480 с.

Приложение

Листинг файла package.json

{
"dependencies": {
"async": "^2.4.1",
"body-parser": "^1.17.2",
"connect-mongo": "^1.3.2",
"cookie-parser": "^1.4.3",
"express": "^4.15.3",
"express-session": "^1.15.3",
"mongoose": "^4.10.3",
"serve-favicon": "^2.4.3"
}
}

Листинг файла config.js

module.exports = {
port: 49002,
mongodb: {
"main": "mongodb://127.0.0.1:27017/main"
},
session: "aXVmN7y{Xw5|9zN%El2m"
};

Листинг файла createDB.js

varmongoose = require('./lib/mongoose');
varasync = require('async');

async.series([
open,
dropDatabase,
requireModels,
createUsers,
createCategory,
createGoods
], function(err) {
console.log(arguments);
});

functionopen(callback) {
mongoose.connection.on('open', callback);
}

functiondropDatabase(callback) {
vardb = mongoose.connection.db;
db.dropDatabase(callback);
}

functionrequireModels(callback) {
require('./models/user');
require('./models/category');
require('./models/goods');

async.each(Object.keys(mongoose.models), function(modelName, callback) {
mongoose.models[modelName].ensureIndexes(callback);
}, callback);
}

functioncreateUsers(callback) {

varusers = [
{username: 'Вася', password: 'supervasya'},
{username: 'Петя', password: '123'},
{username: 'admin', password: 'thetruehero'}
];

async.each(users, function(userData, callback) {
varuser = newmongoose.models.User(userData);
user.save(callback);
}, callback);
}

functioncreateCategory(callback) {

varcategories = [
{id: 0, name: 'Товары для собак', img: 'dogs.jpg'},
{id: 1, name: 'Товары для котов', img: 'cats.jpg'}
];

async.each(categories, function(categoryData, callback) {
varcategory = newmongoose.models.Category(categoryData);
category.save(callback);
}, callback);
}

functioncreateGoods(callback) {

vargoods = [
{id: 0, category: 0, price: 10, name: 'Корм для собак Чаппи', img: 'dogs1.jpg'},
{id: 1, category: 0, price: 20, name: 'Корм для собак Trainer Natural Mini Adult', img: 'dogs2.jpg'},
{id: 2, category: 1, price: 30, name: 'Корм Brit Care Crazy для котят', img: 'cats1.jpg'},
{id: 3, category: 0, price: 40, name: 'Корм для собак Acana PACIFICA DOG', img: 'dogs3.jpg'},
{id: 4, category: 1, price: 50, name: 'Корм для кошек Gourmet Кусочки в соусе', img: 'cats2.jpg'},
{id: 5, category: 1, price: 60, name: 'Консервированный корм для кошек Friskies', img: 'cats3.jpg'},
];

async.each(goods, function(goodsData, callback) {
vargood = newmongoose.models.Goods(goodsData);
good.save(callback);
}, callback);
}

Листинг файла error.js

varutil = require('util');
varhttp = require('http');

// ошибки для выдачи посетителю
functionHttpError(status, message) {
Error.apply(this, arguments);
Error.captureStackTrace(this, HttpError);

this.status= status;
this.message= message || http.STATUS_CODES[status] || "Error";
}

util.inherits(HttpError, Error);

HttpError.prototype.name= 'HttpError';

exports.HttpError = HttpError;

Листинг файла index.js

'use strict';

consthttp = require('http'),
express = require('express'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
favicon = require('serve-favicon'),
config= require('./config'),
path = require('path');

varapp = express(), port, server;

app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(cookieParser());
app.use(bodyParser.urlencoded({limit: '<100mb>'}));
app.use(require('./middleware/session'));
app.use(require('./middleware/sendHttpError'));
app.use(require('./middleware/loadUser'));

port = config.port|| 49001;
app.set('port', port);

server = http.createServer(app);

server.on('error', serverError);

server.on('listening', serverListening);

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

app.use(express.static(path.join(__dirname, 'public')));

server.listen(port);

require('./createDB');

functionserverError(error) {
if(error.syscall!== 'listen') {
throwerror;
}

varbind = typeofport === 'string'? 'Pipe '+ port : 'Port '+ port;

if(error.code === 'EACCES') {
console.error(bind + ' requires elevated privileges');
process.exit(1);
} else if(error.code === 'EADDRINUSE') {
console.error(bind + ' is already in use');
process.exit(1);
} else{
throwerror;
}
}

functionserverListening() {
varaddress = server.address(),
bind = typeofaddress === 'string'? 'pipe '+ address : 'port '+ address.port;
console.log('Listening on '+ bind);
}

Листинг файла routes.js

varpath = require('path'),
checkAuth = require('./middleware/checkAuth');

module.exports = function(app) {

app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/public/template/index.html'));
});

app.post('/register', require('./routes/register').post);

app.post('/login', require('./routes/login').post);

app.get('/login', checkAuth, require('./routes/login').get);

app.post('/logout', require('./routes/logout').post);

app.get('/categories', checkAuth, require('./routes/categories').get);

app.post('/goods', checkAuth, require('./routes/goods').post);

};

Листинг файла lib/mongoose.js

varmongoose = require('mongoose');
var config= require('../config');

mongoose.connect(config.mongodb.main);

module.exports = mongoose;

Листинг файла lib/session_store.js

constsession = require('express-session');
constMongoStore = require('connect-mongo')(session);

module.exports = function(mongoose) {
return newMongoStore({ mongooseConnection: mongoose.connection})
};

Листинг файла middleware/checkAuth.js

module.exports = function(req, res, next) {
if(!req.session.user) {
returnres.status(401).send({ error: "Вы не авторизованы"});
}

next();
};

Листинг файла middleware/loadUser.js

varUser = require('../models/user').User;

module.exports = function(req, res, next) {
req.user = res.locals.user = null;

if(!req.session.user) returnnext();

User.findById(req.session.user, function(err, user) {
if(err) returnnext(err);

req.user = res.locals.user = user;
next();
});
};

Листинг файла middleware/sendHttpError.js

module.exports = function(req, res, next) {
res.sendHttpError = function(error) {
res.status(error.status);
if(res.req.headers['x-requested-with'] == 'XMLHttpRequest') {
res.json(error);
} else{
res.send(error.message);
}
};
next();
};

Листинг файла middleware/session.js

varsession = require('express-session'),
config= require('../config'),
mongoose = require('../lib/mongoose'),
sessionStore = require('../lib/session_store')(mongoose);

module.exports = session({
secret: config.session,
name: 'sid',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: null
},
store: sessionStore
});

Листинг файла models/category.js

varutil = require('util');

varmongoose = require('../lib/mongoose'),
Schema = mongoose.Schema;

varschema = newSchema({
id: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
img: {
type: String,
required: true
}
});

exports.Category= mongoose.model('Category', schema);

Листинг файла models/goods.js

varutil = require('util');

varmongoose = require('../lib/mongoose'),
Schema = mongoose.Schema;

varschema = newSchema({
id: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
img: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
category: {
type: Number,
required: true
},
quantity: {
type: Number,
required: true,
default: 0
}
});

exports.Goods= mongoose.model('Goods', schema);

Листинг файла models/user.js

varcrypto = require('crypto');
varasync = require('async');
varutil = require('util');

varmongoose = require('../lib/mongoose'),
Schema = mongoose.Schema;

varschema = newSchema({
username: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});

schema.methods.encryptPassword = function(password) {
returncrypto.createHmac('sha1', this.salt).update(password).digest('hex');
};

schema.virtual('password')
.set(function(password) {
this._plainPassword= password;
this.salt= Math.random() + '';
this.hashedPassword= this.encryptPassword(password);
})
.get(function() {
return this._plainPassword;
});


schema.methods.checkPassword = function(password) {
return this.encryptPassword(password) === this.hashedPassword;
};

schema.statics.authorize = function(username, password, callback) {
varUser = this;

async.waterfall([
function(callback) {
User.findOne({username: username}, callback);
},
function(user, callback) {
if(user) {
if(user.checkPassword(password)) {
callback(null, user);
} else{
callback(newAuthError("Неправильный пароль"));
}
} else{
callback(newAuthError("Пользователь с таким логином не зарегистрирован"));
}
}
], callback);
};

schema.statics.register = function(username, password, repeat, callback) {
varUser = this;

async.waterfall([
function(callback) {
User.findOne({username: username}, callback);
},
function(user, callback) {
if(user) {
callback(newAuthError("Такой пользователь уже зарегистрирован"));
} else{
if(password === repeat) {
varuser = newUser({username: username, password: password});
user.save(function(err) {
if(err) returncallback(err);
callback(null, user);
});
} else{
callback(newAuthError("Пароли не совпадают"));
}
}
}
], callback);
};

exports.User= mongoose.model('User', schema);


functionAuthError(message) {
Error.apply(this, arguments);
Error.captureStackTrace(this, AuthError);

this.message= message;
}

util.inherits(AuthError, Error);

AuthError.prototype.name= 'AuthError';

exports.AuthError = AuthError;

Листинг файла public/app.js

'use strict';

angular
.module('myApp', ['ui.router'])
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider.state('home', {
url: '/home',
templateUrl: 'template/home.html',
pageTitle: 'Главная',
params: {}
}).state('chat', {
url: '/chat',
templateUrl: 'template/chat.html',
pageTitle: 'Чат',
params: {}
}).state('login', {
url: '/login',
templateUrl: 'template/login.html',
pageTitle: 'Вход',
params: {}
}).state('logout', {
url: '/logout',
pageTitle: 'Выход',
params: {},
controller: function($http, $state) {
$http({method: 'POST', url: '/logout'});
$state.go('home');
window.location.reload();
}
}).state('categories', {
url: '/categories',
templateUrl: 'template/categories.html',
pageTitle: 'Каталог',
params: {},
resolve:{
access: function($http){
return$http({method: 'GET', url: '/login'}).then(function(res) {
returnres;
}, function(res) {
returnres;
});
},
}
}).state('goods', {
url: '/goods',
templateUrl: 'template/goods.html',
pageTitle: 'Товары',
params: {
category: null
},
resolve:{
access: function($http){
return$http({method: 'GET', url: '/login'}).then(function(res) {
returnres;
}, function(res) {
returnres;
});
},
}
}).state('cart', {
url: '/cart',
templateUrl: 'template/cart.html',
pageTitle: 'Корзина',
params: {
category: null
},
resolve:{
access: function($http){
return$http({method: 'GET', url: '/login'}).then(function(res) {
returnres;
}, function(res) {
returnres;
});
},
}
}).state('about', {
url: '/about',
templateUrl: 'template/about.html',
pageTitle: 'О нас',
params: {}
}).state('shipping', {
url: '/shipping',
templateUrl: 'template/shipping.html',
pageTitle: 'Доставка',
params: {}
}).state('profile', {
url: '/profile',
templateUrl: 'template/profile.html',
pageTitle: 'Личный кабинет',
params: {}
});

$urlRouterProvider.otherwise('/home');

$stateProvider.state("otherwise", {url: '/otherwise'});
}])

.run(['$state', '$rootScope', '$stateParams', 'userService', '$http',
function($state, $rootScope, $stateParams, userService, $http) {
$rootScope.$state= $state;
$rootScope.$stateParams= $stateParams;
$rootScope.$on("$includeContentLoaded", function() {
$('body').scrollTop(0);
});
$rootScope.$on('$stateChangeSuccess', function() {
userService.navChange();
});

$http({
url: "/login",
method: "GET"
}).then(function(res) {
userService.username= res.data.username;
}, function(res) {
userService.username= false;
});

}])
.service('userService', function() {
return{
goods: null,
username: null,
order: {}
};
})
.controller('navController', function($state, userService, $http, $scope, $rootScope) {
varvm = this;

userService.navChange = function() {
vartabs = {
home: {name: 'Главная', url: 'home', active: false},
login: {name: 'Авторизация', url: 'login', active: false},
logout: {name: 'Выход', url: 'logout', active: false},
categories: {name: 'Каталог', url: 'categories', active: false},
cart: {name: 'Корзина', url: 'cart', active: false},
about: {name: 'О нас', url: 'about', active: false},
profile: {name: 'Личный кабинет', url: 'profile', active: false},
shipping: {name: 'Доставка', url: 'shipping', active: false}
};
if(tabs[$state.current.name]) {
tabs[$state.current.name].active= true;
} else{
tabs['categories'].active= true;
}
if(userService.username) {
vm.tabs= [tabs.home, tabs.about, tabs.shipping, tabs.categories, tabs.cart, tabs.profile, tabs.logout];
} else{
vm.tabs= [tabs.home, tabs.about, tabs.shipping, tabs.login];
}
}
})
.controller('homeController', function($state, $scope, $rootScope) {
varvm = this;
})
.controller('aboutController', function($state, $scope, $rootScope) {
varvm = this;
})
.controller('shippingController', function($state, $scope, $rootScope) {
varvm = this;
})
.controller('profileController', function($state, $scope, $rootScope) {
varvm = this;
})
.controller('loginController', function($state, $http, userService, $scope, $rootScope) {
varvm = this;
vm.loginRegister= true;
vm.registerForm = function() {
vm.loginRegister= false;
vm.error= false;
};
vm.loginForm = function() {
vm.loginRegister= true;
vm.error= false;
};
vm.register = function() {
if(vm.password=== vm.repeat) {
$http({
url: "/register",
method: "POST",
headers: {
"content-type": 'application/x-www-form-urlencoded'
},
data: 'username='+ vm.username+ '&password='+ vm.password+ '&repeat='+ vm.repeat
}).then(function(res) {
userService.username= vm.username;
userService.navChange();
$state.go('categories');
}, function(res) {
if(res.status === 403) {
vm.error= res.data.message;
} else{
$state.go('error');
}
});
} else{
vm.error= 'Введенные пароли не совпадают';
}
};
vm.login = function() {
$http({
url: "/login",
method: "POST",
headers: {
"content-type": 'application/x-www-form-urlencoded'
},
data: 'username='+ vm.username+ '&password='+ vm.password
}).then(function(res) {
userService.username= vm.username;
userService.navChange();
$state.go('categories');
}, function(res) {
if(res.status === 403) {
vm.error= res.data.message;
} else{
$state.go('error');
}
});
}
})
.controller('categoriesController', function($state, $http, $scope, $rootScope) {
varvm = this;

$state.current.resolve.access($http).then(function(res){
if(res.status === 401) {
vm.error= res.data.error;
}
});

$http({
url: "/categories",
method: "GET"
}).then(function(res) {
vm.categories= res.data;
}, function(res) {
vm.error= res.data.error;
});

vm.openCategory = function(id) {
$state.go('goods', { category: id })
};
})
.controller('goodsController', function($state, $http, $scope, $rootScope, $stateParams, userService) {
varvm = this;

$state.current.resolve.access($http).then(function(res){
if(res.status === 401) {
vm.error= res.data.error;
}
});

$http({
url: "/goods",
method: "POST",
headers: {
"content-type": 'application/x-www-form-urlencoded'
},
data: 'category='+ $stateParams.category
}).then(function(res) {
vargood, i;
vm.goods= res.data;
for(good inuserService.order) {
for(i = 0; i < vm.goods.length; i++) {
if(vm.goods[i].id == good) {
vm.goods[i].quantity= userService.order[good];
}
}
}
}, function(res) {
$state.go('home');
});

vm.addToCart = function(id, price, quantity) {
userService.order[id] = quantity;
}
})
.controller('cartController', function($state, $http, $scope, $rootScope, $stateParams, userService) {
varvm = this;
vm.all= 0;
vm.state= 0;
vm.locations= [{name:'Минск'},{name:'Минская область'},{name:'Беларусь'}];


$state.current.resolve.access($http).then(function(res){
if(res.status === 401) {
vm.error= res.data.error;
}
});

$http({
url: "/goods",
method: "POST"
}).then(function(res) {
vargood, i;
vm.goods= res.data;
vm.shipping= 0;
for(good inuserService.order) {
for(i = 0; i < vm.goods.length; i++) {
if(vm.goods[i].id == good) {
vm.goods[i].quantity= userService.order[good];
}
}
}
for(i = 0; i < vm.goods.length; i++) {
vm.all+= vm.goods[i].quantity* vm.goods[i].price;
}
vm.locationChange();
}, function(res) {
$state.go('home');
});

vm.locationChange = function() {
if(vm.state.name === "Минская область") {
if(vm.all< 200) {
vm.shipping= 20;
} else{
vm.shipping= 0;
}
} else if(vm.state.name === "Беларусь") {
if(vm.all< 500) {
vm.shipping= 50;
} else{
vm.shipping= 0;
}
} else{
if(vm.all< 100) {
vm.shipping= 10;
} else{
vm.shipping= 0;
}
}
vm.resume= vm.all+ vm.shipping;
}
});

Листинг файла public/template/about.html

<div ng-controller="aboutController as vm">
<p class="lead">За шесть лет своей деятельности мы прошли большой путь от небольшой торговой точки до полноценного магазина товаров для животных. За это время нам удалось не только значительно расширить ассортимент представленных зоотоваров, но и самое важное - завоевать популярность среди владельцев домашних животных. Отличительными чертами нашего магазина стали умеренные цены, качественное обслуживание и большой выбор товаров для животных от ведущих мировых производителей.</p>
<p class="lead">Магазин имеет площадь 150 м2, разделен на секции. В настоящий момент ассортимент магазина постоянно расширяется и изменяется. В продаже всегда есть товары, которые популярны благодаря своему отличному соотношению цены и качества. Мы можем предложить вам только те товары, которые уже проверены временем и пользуются заслуженной популярностью у владельцев домашних животных. Для постоянных клиентов действуют скидочные карты 3-5%, проводятся акции и предлагаются скидки на различные категории зоотоваров.</p>
<p class="lead">Наш телефон для связи: + 375 29 187-55-64</p>
</div>

Листинг файла public/template/cart.html

<div ng-controller="cartController as vm">
<div ng-if="vm.error">
<div class="card card-inverse card-danger text-center">
<div class="card-block">
<blockquote class="card-blockquote">Вы не авторизовались и не можете заказывать товары</blockquote>
</div>
</div>
</div>
<div ng-if="!vm.error">
<table class="table">
<thead>
<tr>
<th>Товар</th>
<th>Количество</th>
<th>Цена</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in vm.goods">
<td><img class="categoryImgTumb" src="/img/{{item.img}}"> {{item.name}}</td>
<td>{{item.quantity}}</td>
<td>{{item.quantity* item.price}}</td>
</tr>
<tr>
<td>Итого</td>
<td></td>
<td>{{vm.all}}</td>
</tr>
</tbody>
</table>
<form ng-if="vm.all">
<div class="form-group">
<select class="form-control" ng-options="location.name for location in vm.locations" ng-init="vm.state=vm.locations[0]" ng-model="vm.state" ng-change="vm.locationChange()" required>
</select>
</div>
<div class="form-group">
<input class="form-control" placeholder="Ваш адрес" ng-model="vm.address" required>
</div>
<div class="form-group">
<span>Стоимость доставки: {{vm.shipping}}</span>
</div>
<div class="form-group">
<span>Итого включая доставку: {{vm.resume}}</span>
</div>
<div class="form-group">

</div>
<div class="form-group">
<button class="btn btn-primary btn-block">Подтвердить заказ</button>
</div>
</form>
</div>
</div>

Листинг файла public/template/categories.html

<div ng-controller="categoriesController as vm">
<div ng-if="vm.error">
<div class="card card-inverse card-danger text-center">
<div class="card-block">
<blockquote class="card-blockquote">Вы не авторизовались и не можете заказывать товары</blockquote>
</div>
</div>
</div>
<div ng-if="!vm.error">
<div class="row">
<div class="col-md-6 col-xs-12 mb-3 cart" ng-repeat="item in vm.categories">
<a ng-click="vm.openCategory(item.id)">
<div class="text-center"><b>{{item.name}}</b></div>
<div><img class="categoryImg" src="/img/{{item.img}}"></div>
</a>
</div>
</div>
</div>
</div>

Листинг файла public/template/goods.html

<div ng-controller="goodsController as vm">
<div ng-if="vm.error">
<div class="card card-inverse card-danger text-center">
<div class="card-block">
<blockquote class="card-blockquote">Вы не авторизовались и не можете заказывать товары</blockquote>
</div>
</div>
</div>
<div ng-if="!vm.error">
<div class="row">
<div class="col-md-4 col-xs-12 mb-3" ng-repeat="item in vm.goods">
<div class="text-center"><b>{{item.name}}</b></div>
<div class="mb-3"><img class="categoryImg" src="/img/{{item.img}}"></div>
<div class="text-center mb-3"><b>Цена за штуку: {{item.price}}</b></div>
<form ng-submit="vm.addToCart(item.id, item.price, item.quantity)" class="input-group"><input ng-model="item.quantity" type="number" class="form-control"><button type="submit" class="input-group-addon cart">Добавить в корзину</button></form>
</div>
</div>
</div>
</div>

Листинг файла public/template/home.html

<div ng-controller="homeController as vm">
<p class="lead">Мы постоянно расширяем свой ассортимент, поставляя только качественные товары для животных популярных брендов. Всегда умеренные цены, вежливый и отзывчивый персонал, оперативная работа и гарантия качества – все это наш зоомагазин</p>
<p class="lead">Самым главным достижением нашего зоомагазина является доверие и любовь владельцев домашних животных. Для постоянных клиентов у нас предусмотрены скидки, проводятся регулярные акции, где можно приобрести товары по специальным ценам.Интернет-магазин зоотоваров предлагает только ту продукцию, которая проверена временем и имеет хорошие отзывы покупателей. Мы тщательно следим за качеством, потому что ценим своих клиентов.</p>
<p class="lead">Мы предлагаем большой выбор сухих кормов Acana, Orijen, Royal Canin, Bosch, Hills, Eukanuba и Purina Pro Plan. Разнообразить рацион домашних питомцев помогут консервы, паучи и лакомства Зоогурман, CatNatura, Dr Alders (Германия), Brit, Molina, Triol и многие другие.Кроме того, в нашем зоомагазине вы найдете профессиональную амуницию и средства по уходу, которые идеально подойдут для животных, участвующих в выставках. Цепочки-кобры, ринговые водилки и поводки, ринговки, шампуни для шерсти, расчески – все то, что необходимо для удачного выступления.</p>
<p class="lead">Ассортимент игрушек для котов и собак приятно порадует своим разнообразием. Вы можете приобрести кольца, мячики, кусалки-жгуты и разнообразные игрушки Kinologprofi, Puller, Rogz, Trixie, Kong, которые сделают досуг питомцев веселым и разнообразным.Обратите внимание на когтеточки, переноски и домики Trixie, завоевавшие популярность благодаря отличному качеству, долгой службе и привлекательности для домашних любимцев.</p>
<p class="lead">Радуйте своих питомцев, а мы будем радовать вас приятными скидками, умеренными ценами и новыми товарами для животных высокого качества.</p>
<p class="lead">Авторизуйтесь на сайте, что бы иметь возможность приобретать наши товары.</p>
</div>

Листинг файла public/template/index.html

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title ng-bind="$state.current.pageTitle"></title>

<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

<link rel="stylesheet" href="/bootstrap.css"/>

<script src="/jquery-3.2.1.min.js"></script>
<script src="/angular.js"></script>
<script src="/angular-ui-router.js"></script>
<script src="/app.js"></script>
</head>
<body>
<div class="container">
<div ng-controller="navController as vm">
<ul class="nav nav-pills nav-fill border-bottom-1 mt-2 mb-5">
<img class="logo" src="/img/logo.jpg">
<li class="nav-item" ng-repeat="nav in vm.tabs">
<a class="nav-link {{nav.active ? 'active' : ''}}" ui-sref="{{nav.url}}">{{nav.name}}</a>
</li>
</ul>
</div>

<div ui-view></div>
</div>
<footer class="footer">
<div class="container">
<span class="text-muted">г. Минск, пр. Независимости 46, "Наш зоомагазин". 2017</span>
</div>
</footer>
</body>
</html>

Листинг файла public/template/login.html

<div ng-controller="loginController as vm">
<form class="form-horizontal login-form" ng-show="vm.loginRegister" ng-submit="vm.login()">
<div class="form-group">
<input class="form-control" placeholder="Ваш логин" ng-model="vm.username" required>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Пароль" ng-model="vm.password" required>
</div>
<div class="form-group" ng-show="vm.error">
<div class="card card-inverse card-danger text-center">
<div class="card-block">
<blockquote class="card-blockquote">{{vm.error}}</blockquote>
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary cart mr-3">Авторизоваться</button><button type="button" class="btn cart" ng-click="vm.registerForm()">Регистрация</button>
</div>
</form>
<form class="form-horizontal login-form" ng-show="!vm.loginRegister" ng-submit="vm.register()">
<div class="form-group">
<input class="form-control" placeholder="Ваш логин" ng-model="vm.username" required>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Пароль" ng-model="vm.password" required>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="Повторите пароль" ng-model="vm.repeat" required>
</div>
<div class="form-group" ng-show="vm.error">
<div class="card card-inverse card-danger text-center">
<div class="card-block">
<blockquote class="card-blockquote">{{vm.error}}</blockquote>
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary cart mr-3">Зарегистрироваться</button><button type="button" class="btn cart" ng-click="vm.loginForm()">Авторизация</button>
</div>
</form>
</div>

Листинг файла public/template/shipping.html

<div ng-controller="shippingController as vm">
<p class="lead">Бесплатная доставка товаров по г. Минску в пределах МКАД от 100 руб. В остальных случаях стоимость доставки по г. Минску составляет 10 руб.</p>
<p class="lead">Бесплатная доставка товаров по Минскому району от 200 руб. В остальных случаях стоимость доставки по Минскому району составляет 20 руб.</p>
<p class="lead">Бесплатная доставка товаров по остальной части Беларуси от 500 руб. В остальных случаях стоимость доставки по Беларуси составляет 50 руб.</p>
<p class="lead">Доставка товаров по Минску осуществляется ежедневно: пн-пт: 12:00–17:30 и 18:30–22:00, сб-вс: 17:30–22:00.</p>
<p class="lead">Оплата осуществляется наличными курьеру по факту доставки</p>
</div>

Листинг файла routes/categories.js

varCategory = require('../models/category').Category;

exports.get = function(req, res) {
Category.find({}).exec(function(err, categories) {
if(err) returncallback(err);
res.json(categories);
});
};

Листинг файла routes/goods.js

varGoods = require('../models/goods').Goods;
varHttpError = require('../error').HttpError;

exports.post = function(req, res, next) {
if(req.body && req.body.category) {
Goods.find({category: req.body.category>= 0 ? req.body.category: 0 }).sort({ id: 1 }).exec(function(err, goods) {
if(err) returnnext(newHttpError(404, err.message));
res.json(goods);
});
} else{
Goods.find().sort({ id: 1 }).exec(function(err, goods) {
if(err) returnnext(newHttpError(404, err.message));
res.json(goods);
});
}
};

Листинг файла routes/login.js

varUser = require('../models/user').User;
varHttpError = require('../error').HttpError;
varAuthError = require('../models/user').AuthError;
varasync = require('async');

exports.get = function(req, res) {
res.json(req.session.user);
};

exports.post = function(req, res, next) {
varusername = req.body.username;
varpassword = req.body.password;

User.authorize(username, password, function(err, user) {
if(err) {
if(err instanceofAuthError) {
returnres.status(403).json({message: err.message});
} else{
returnnext(err);
}
}

req.session.user= user;
res.send(req.session.user);

});

};

Листинг файла routes/logout.js

exports.post = function(req, res) {
req.session.destroy();
res.redirect('/');
};

Листинг файла routes/register.js

varUser = require('../models/user').User;
varHttpError = require('../error').HttpError;
varAuthError = require('../models/user').AuthError;
varasync = require('async');

exports.post = function(req, res, next) {
varusername = req.body.username;
varpassword = req.body.password;
varrepeat = req.body.repeat;

User.register(username, password, repeat, function(err, user) {
if(err) {
if(err instanceofAuthError) {
returnres.status(403).json({message: err.message});
} else{
returnnext(err);
}
}

req.session.user= user;
res.send(req.session.user);

});

};

Наши рекомендации