Browse Source

js版本

dev
topuser 5 years ago
commit
06ec52b00b
  1. 2
      .gitignore
  2. 3
      .vscode/settings.json
  3. 60
      app.js
  4. 90
      bin/www
  5. 12
      codes/sys.js
  6. 32
      codes/user.js
  7. 11
      config.js
  8. 33
      controler/index.js
  9. 258
      controler/menus/index.js
  10. 71
      controler/menus/test.js
  11. 181
      controler/user/index.js
  12. 10
      controler/views/index.js
  13. 8
      global/index.js
  14. 45
      global/res.util.js
  15. 43
      init/index.js
  16. 11
      init/save/open.sql
  17. 13
      init/sql/category.sql
  18. 14
      init/sql/user.sql
  19. 30
      init/util/db.js
  20. 30
      init/util/get-sql-content-map.js
  21. 20
      init/util/get-sql-map.js
  22. 28
      init/util/walk-file.js
  23. 98
      models/menus.js
  24. 110
      models/user-info.js
  25. 4234
      package-lock.json
  26. 38
      package.json
  27. 9
      public/home.pug
  28. 3
      public/static/css/index.css
  29. 10
      public/static/css/style.css
  30. 1
      public/static/js/home.js
  31. 17
      public/static/js/index.js
  32. 1
      public/template/footer.pug
  33. 1
      public/template/header.pug
  34. 19
      public/template/layout.pug
  35. 3
      public/ui/login.pug
  36. 13
      readme.md
  37. 14
      routes/api/menus.js
  38. 14
      routes/api/user.js
  39. 34
      routes/index.js
  40. 12
      routes/views/home.js
  41. 84
      services/menus.js
  42. 176
      services/user-info.js
  43. 0
      test/host.spec.js
  44. 14
      test/user.spec.js
  45. 66
      utils/datetime.js
  46. 162
      utils/db-util.js
  47. 26
      utils/index.js
  48. 37
      utils/type.js

2
.gitignore

@ -0,0 +1,2 @@
node_modules
.idea

3
.vscode/settings.json

@ -0,0 +1,3 @@
{
"sqltools.connections": []
}

60
app.js

@ -0,0 +1,60 @@
// require('module-alias/register')
require('./global/index.js');
const Koa = require('koa');
const jwt = require('koa-jwt');
const views = require('koa-views');
const serve = require('koa-static');
const app = new Koa();
const cors = require('@koa/cors');
const koaBody = require('koa-body');
const router = require('./routes/index.js');
const config = require('./config.js');
const path = require('path')
app.use(serve(path.join(__dirname + '/public')));
app.use(views(path.join(__dirname + '/public'), {
extension: 'pug'
}));
app.use(cors());
// Custom 401 handling if you don't want to expose koa-jwt errors to users
app.use(async (ctx, next) => {
return next().catch((err) => {
if (401 == err.status) {
ctx.status = 401;
ctx.body = {
message: 'Protected resource, use Authorization header to get access\n',
data: null,
code: 401
};
} else {
throw err;
}
});
});
// https://www.v2ex.com/t/320710有关token不需要状态,可以多人登录同一个账号
app.use(jwt({
secret: config.share_key
}).unless({
path: [/^\/api\/login/, /^\/api\/register/, /^((?!\/api).)*$/,/^\/api\/menus\/getAll/]
}));
// logger
app.use(async (ctx, next) => {
const start = new Date()
await next()
const ms = new Date() - start
log(`${ctx.method} ${ctx.url} - ${ms}ms`)
});
// 请求解析
app.use(koaBody());
// 路由注册
app.use(router.routes()).use(router.allowedMethods());
// error-handling
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
module.exports = app

90
bin/www

@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('demo: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.callback());
/**
* 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);
}

12
codes/sys.js

@ -0,0 +1,12 @@
/**
* 逻辑文案管理
*/
const sysCode = {
SUCCESS: "success",
Fail: "fail",
}
module.exports = sysCode

32
codes/user.js

@ -0,0 +1,32 @@
/**
* 逻辑文案管理
*/
const userCode = {
ERROR_USER_NAME: '用户名格式为6-16位的小写字母,包括-、_',
ERROR_EMAIL: '请输入正确的邮箱地址',
ERROR_PASSWORD: '密码长度应该为6-16',
ERROR_PASSWORD_CONFORM: '两次密码不一致',
ERROR_SYS: '系统错误',
FAIL_EMAIL_IS_EXIST: '邮箱已被注册',
FAIL_USER_NAME_IS_EXIST: '用户名已被注册',
FAIL_USER_NAME_OR_PASSWORD_ERROR: '用户名或登录密码错误',
FAIL_USER_NO_LOGIN: '用户未登录',
FAIL_USER_NO_EXIST: '用户不存在',
ERROR_USER_EMPTY: '用户名不能为空',
ERROR_MORE_USER: '查询到多个用户',
}
module.exports = userCode

11
config.js

@ -0,0 +1,11 @@
module.exports = {
db: {
host: '47.244.111.211',
port: '3306',
user: 'noderest',
password: '8WBtsejYGiEESJMh',
database: 'noderest',
connectTimeout:3000
},
share_key: 'dsadasdasd'
}

33
controler/index.js

@ -0,0 +1,33 @@
const fs = require('fs');
const path = require('path');
const Bean = require('../utils/index.js');
// 控制器收集规则
const routes = fs.readdirSync(path.resolve(__dirname));
const controler = {};
routes.forEach(route => {
if (fs.statSync(path.resolve(__dirname, route)).isDirectory()) {
const deep = Bean.readFileDeep(path.resolve(__dirname, route)).file;
controler[route] = {};
deep.forEach(file => {
Object.keys(require(file)).forEach(key => {
if (Object.keys(controler[route]).includes(key)) {
let str = "注意有相同的控制器:{" + route + ':' + key + '}';
throw str
}
})
controler[route] = {
...controler[route],
...require(file)
}
})
}
if (fs.statSync(path.resolve(__dirname, route)).isFile()) {
if (route !== 'index.js') {
controler[route.slice(0, -3)] = require(path.resolve(__dirname, route));
}
}
})
module.exports = controler

258
controler/menus/index.js

@ -0,0 +1,258 @@
const menuServices = require('../../services/menus.js')
function findeBelong(data, index) {
let array = [];
for (let i = data.length - 1; i >= 0; i--) {
if (data[i].belongto == index) {
const obj = {
id: data[i].id,
name: data[i].name,
desc: data[i].about,
children: []
}
array.push(obj);
data.splice(i, 1);
}
}
return array;
}
function sortTree(arr, all) {
let tree = (arr, all) => {
for (let i = 0; i < arr.length; i++) {
let item = findeBelong(all, arr[i].id);
for (let j = 0; j < item.length; j++) {
const obj = {
id: item[j].id,
name: item[j].name,
desc: item[j].desc,
children: []
}
arr[i].children.push(obj);
}
tree(arr[i].children, all)
}
}
tree(arr, all)
}
module.exports = {
/**
* 获取所有的菜单
* @param ctx
* @param next
* @returns {Promise<void>}
*/
async getAll(ctx, next) {
let result = {
success: false,
message: '',
data: null
}
let getResult = await menuServices.getAllMenus();
if (getResult && Array.isArray(getResult)) {
let menus = JSON.parse(JSON.stringify(getResult));
let fa = findeBelong(menus, '0');
sortTree(fa, menus);
if (menus.length > 0) {
result.success = false;
result.message = '菜单不完整';
ctx.body = result
return
} else {
result.success = true;
result.message = 'success';
result.data = fa;
}
} else{
result.success = false;
result.message = 'fail';
ctx.body = result
return ;
}
//menus有长度表示还有菜单没消耗完
ctx.body = result
},
/**
* 创建菜单
* @param ctx
* @param next
* @returns {Promise<void>}
*/
async createOne(ctx, next) {
let result = {
success: false,
message: '',
data: null
}
let formData = ctx.request.body;
//验证参数的必需
let menuResult = await menuServices.validatorNeed(formData, [
{key: 'name', error: '标题不能为空', validate: (data) => data == undefined},
{key: 'belongto', error: '没有所属ID', validate: (data) => data == undefined}
]);
if (!menuResult.success) {
result.success = false;
result.message = menuResult.message;
ctx.body = result;
return;
} else {
// 验证所属ID是数据库中的
if (Number(formData.belongto) > 0) {
let allMenus = await menuServices.getAllMenus();
let allID = allMenus.map(v => v.id);
if (allID.indexOf(formData.belongto) == -1) {
result.success = false;
result.message = '没有该菜单';
ctx.body = result;
return;
}
}
let createResult = await menuServices.createMenu(formData);
if (createResult && createResult.insertId * 1 > 0) {
result.success = true;
result.message = '创建成功';
} else {
result.success = true;
result.message = '系统错误';
ctx.body = result;
return;
}
}
ctx.body = result;
},
/**
*
* 修改菜单
*/
async updateOne(ctx, next) {
let result = {
success: false,
message: '',
data: null
}
let formData = ctx.request.body;
//验证参数的必需
let menuResult = await menuServices.validatorNeed(formData, [
{key: 'id', error: 'id不能为空', validate: (data) => data == undefined},
{
key: 'id', error: '没有这个菜单', validate: async (data) => {
let allMenus = await menuServices.getAllMenus();
let allID = allMenus.map(v => v.id);
if (allID.indexOf(formData.id) == -1) {
return true;
}
return false;
}
},
{key: 'belongto', error: '没有父类', validate: (data) => data == undefined || data < 0},
{
key: 'belongto', error: '没有父ID', validate: async (data) => {
if (data == 0) {
return false;
}
let allMenus = await menuServices.getAllMenus();
let allID = allMenus.map(v => v.id);
if (allID.indexOf(formData.belongto) == -1) {
return true;
}
return false;
}
},
{key: 'id', error: 'ID不能与父ID相同', validate: (data) => formData.id == formData.belongto}
]);
if (!menuResult.success) {
result.success = false;
result.message = menuResult.message;
ctx.body = result;
return;
} else {
let updateResult = await menuServices.updateMenu(formData);
if (updateResult) {
result.success = true;
result.message = 'success';
} else {
result.success = false;
result.message = 'fail';
}
}
ctx.body = result;
},
/**
* 删除菜单
*/
async deleteOne(ctx, next) {
let result = {
success: false,
message: '',
data: null
}
let formData = ctx.request.body;
//验证参数的必需
let menuResult = await menuServices.validatorNeed(formData, [
{key: 'id', error: 'id不能为空', validate: (data) => data == undefined},
{
key: 'id', error: '没有这个菜单', validate: async (data) => {
let allMenus = await menuServices.getAllMenus();
let allID = allMenus.map(v => v.id);
if (allID.indexOf(formData.id) == -1) {
return true;
}
return false;
}
},
{
key: 'id', error: '这个菜单存在子菜单,替换ID出错', validate: async (data) => {
let judgeResult = await menuServices.judgeID(data);
if (formData.fix==0){
return false;
}
if(judgeResult&&!formData.fix){
return true
}else if(judgeResult&&formData.fix){
//替换ID不能跟要删除的ID相同
if (formData.fix==formData.id){
return '替换ID不能跟要删除的ID相同';
}
// 替换ID不能跟需要修改的ID相同
let uID = judgeResult.map(v => v.id);
if (uID.indexOf(formData.fix) != -1) {
return '替换ID不能跟需要修改的ID相同';
}
// 替换ID必须存在与菜单中
let allMenus = await menuServices.getAllMenus();
let allID = allMenus.map(v => v.id);
if (allID.indexOf(formData.fix) == -1) {
return '替换ID必须存在与菜单中';
}else {
return false;
}
}else {
return false
}
}
},
]);
if (!menuResult.success) {
result.success = false;
result.message = menuResult.message;
ctx.body = result;
return;
} else {
let judgeResult = await menuServices.judgeID(formData.id);
let deleteResult = await menuServices.deleteMenu(formData,judgeResult,formData.fix);
if (deleteResult){
//删除成功
result.success = true;
result.message = 'success';
} else {
result.success = false;
result.message = 'fail';
}
}
ctx.body = result;
}
}

71
controler/menus/test.js

@ -0,0 +1,71 @@
// // var data = [
// // {id: 1, address: "安徽", parent_id: 0},
// // {id: 2, address: "江苏", parent_id: 0},
// // {id: 3, address: "合肥", parent_id: 1},
// // {id: 4, address: "庐阳区", parent_id: 3},
// // {id: 5, address: "大杨镇", parent_id: 4},
// // {id: 6, address: "南京", parent_id: 2},
// // {id: 7, address: "玄武区", parent_id: 6},
// // {id: 8, address: "梅园新村街道", parent_id: 7},
// // {id: 9, address: "上海", parent_id: 0},
// // {id: 10, address: "黄浦区", parent_id: 9},
// // {id: 11, address: "外滩", parent_id: 10},
// // {id: 12, address: "安庆", parent_id: 1}
// // ];
// var data = [ { id: 1, name: 'html', belongto: 0, about: '22' },
// { id: 2, name: 'css', belongto: 0, about: '2256' },
// { id: 3, name: 'js', belongto: 1, about: '5422' },
// { id: 4, name: 'js', belongto: 2, about: '5422' },
// { id: 5, name: 'js', belongto: 3, about: '5422' },
// { id: 6, name: 'js', belongto: 1, about: '5422' },
// { id: 7, name: 'js', belongto: 1, about: '5422' },
// { id: 8, name: 'js', belongto: 5, about: '5422' },
// { id: 9, name: 'js', belongto: 8, about: '5422' },
// { id: 10, name: 'js', belongto: 8, about: '5422' },
// { id: 11, name: 'js', belongto: 10, about: '5422' },
// { id: 12, name: 'js', belongto: 11, about: '5422' } ]
//
// function findeBelong(data, index) {
// let array = [];
// for (let i = data.length-1; i >= 0 ; i--) {
// if(data[i].belongto == index){
// const obj = {
// id: data[i].id,
// name: data[i].name,
// desc: data[i].about,
// children: []
// }
// array.push(obj);
// data.splice(i,1);
// }
// }
// return array;
// }
//
// function sortTree(arr,all){
// let tree = (arr,all)=>{
// for (let i = 0; i < arr.length; i++) {
// let item = findeBelong(all,arr[i].id);
// for (let j = 0; j < item.length; j++) {
// const obj = {
// id: item[j].id,
// name: item[j].name,
// desc: item[j].desc,
// children: []
// }
// arr[i].children.push(obj);
// }
// tree(arr[i].children,all)
// }
// }
// tree(arr,all)
// }
// let fa = findeBelong(data,0);
// sortTree(fa,data);
// console.log(fa);
// console.log(data);
// /*
// * 1. 先找出ID==0的菜单
// * 2. 选中第一个ID的菜单,去菜单表中寻找是否有其子菜单,找到之后构建插入然后删除这个,然后递归查这一个的子菜单
// *
// * */

181
controler/user/index.js

@ -0,0 +1,181 @@
const userCode = require('../../codes/user.js')
const userInfoService = require('../../services/user-info')
module.exports = {
/**
* 修改个人信息由于用户名和邮箱比较重要暂不提供修改
* @param {*} ctx
* @param {*} next
*/
async modifyUserinfo(ctx, next) {
let user = ctx.state.user;
let formData = ctx.request.body;
let result = {
message: 'fail',
data: null,
success: false
};
// 根据ID查询
let userResult = await userInfoService.getExistOneByIdOrUserName(user);
if (userResult) {
// 用户信息验证,不校验密码
let validateResult = await userInfoService.validatorNeed(formData, false)
if (!validateResult.success) {
result.success = false;
result.message = validateResult.message;
ctx.body = result
return
}
let r = await userInfoService.update(formData, user.id);
if (r) {
result.success = true;
result.message = 'success';
} else {
result.success = false;
result.message = 'fail';
}
} else {
result.success = false;
result.message = userCode.FAIL_USER_NO_EXIST
}
ctx.body = result
},
async getUserinfo(ctx, next) {
let formData = ctx.state.user;
let result = {
message: '',
data: null,
code: 400
};
let userResult = await userInfoService.getExistOneByIdOrUserName(formData);
if (userResult) {
result.code = 200;
delete userResult.password
result.data = userResult;
} else {
result.message = userCode.FAIL_USER_NO_EXIST
}
ctx.body = result;
},
/**
* 登录操作返回
* @param {*} ctx
* @param {*} next
*/
async login(ctx, next) {
let formData = ctx.request.body;
let result = {
message: 'fail',
data: null,
success: false
}
let validateResult = await userInfoService.validatorNeed(formData)
if (!validateResult.success) {
result.success = false;
result.message = validateResult.message;
ctx.body = result
return
}
// 是否存在一个相同用户
let existOne = await userInfoService.getExistOne(formData);
if (Array.isArray(existOne)) {
if (existOne.length > 1) {
result.success = false;
result.message = userCode.ERROR_MORE_USER;
ctx.body = result;
return;
}
existOne = existOne[0];
// 密码验证,获取token
let token = await userInfoService.validatorPwd(formData, existOne)
if (!token) {
result.success = false;
result = userCode.FAIL_USER_NAME_OR_PASSWORD_ERROR;
ctx.body = result;
return;
} else {
result.success = true;
result.message = 'success';
result.data = token;
}
} else {
result.success = false;
result.message = userCode.FAIL_USER_NO_EXIST;
ctx.body = result;
return;
}
ctx.body = result;
},
/**
* 注册操作
* @param {*} ctx 上下文
* {
* imgurl,
* email,
* password, 必须
* confirmPassword,必须
* username, 必须
* nickname,
* createTime,
* modifiedTime,
* level
* }
*/
async register(ctx, next) {
let formData = ctx.request.body;
let result = {
success: false,
message: 'fail',
data: null
}
// 用户注册信息验证
let validateResult = await userInfoService.validatorSignUp(formData)
if (!validateResult.success) {
result.success = false;
result.message = validateResult.message;
ctx.body = result;
return
}
// 是否存在一个相同用户
let existOne = await userInfoService.getExistOne(formData);
if (Array.isArray(existOne)) {
if (existOne.length > 1) {
result.success = false;
result.message = userCode.ERROR_MORE_USER;
ctx.body = result;
return;
}
existOne = existOne[0];
// 判断是否存在相同用户名或者邮箱
if (existOne.username === formData.username) {
result.message = userCode.FAIL_USER_NAME_IS_EXIST
ctx.body = result
return
}
if (existOne.email === formData.email) {
result.message = userCode.FAIL_EMAIL_IS_EXIST
ctx.body = result
return
}
}
// 创建用户
let userResult = await userInfoService.create({
imgurl: formData.imgurl,
email: formData.email,
password: formData.password,
username: formData.username,
nickname: formData.nickname,
createTime: new Date().getTime(),
level: 1,
})
if (userResult && userResult.insertId * 1 > 0) {
result.success = true;
result.message = 'success'
} else {
result.message = userCode.ERROR_SYS
}
ctx.body = result;
}
}

10
controler/views/index.js

@ -0,0 +1,10 @@
const menus = require('../menus/index.js')
module.exports = {
async home(ctx, next) {
await menus.getAll(ctx, next);
await ctx.render('home', {
data: JSON.stringify(ctx.body.data)
})
}
};

8
global/index.js

@ -0,0 +1,8 @@
/**
*
* 只注册全局变量
*
*/
const getOneRes = require('./res.util.js');
global.log = require('debug')('demo:server');
global.oneRes = getOneRes.message;

45
global/res.util.js

@ -0,0 +1,45 @@
const SuccessTemplate = {
code: 200,
data: null,
message: 'success'
}
const failTemplate = {
code: 400,
data: null,
message: 'fail'
}
const template = {
code: 200,
data: null,
message: 'ok'
}
module.exports = {
success(code, message, data) {
let res = {
...SuccessTemplate
};
code && (res.code = code);
message && (res.message = message);
data && (res.data = data);
return res;
},
fail(code, message, data) {
let res = {
...failTemplate
};
code && (res.code = code);
message && (res.message = message);
data && (res.data = data);
return res;
},
message(code, message, data) {
let res = {
...template
};
code && (res.code = code);
message && (res.message = message);
data && (res.data = data);
return res;
}
}

43
init/index.js

@ -0,0 +1,43 @@
const fs = require('fs');
const getSqlContentMap = require('./util/get-sql-content-map');
const {
query
} = require('./util/db');
// 打印脚本执行日志
const eventLog = function (err, sqlFile, index) {
if (err) {
console.log(`[ERROR] sql脚本文件: ${sqlFile}${index + 1}条脚本 执行失败 o(╯□╰)o !`)
} else {
console.log(`[SUCCESS] sql脚本文件: ${sqlFile}${index + 1}条脚本 执行成功 O(∩_∩)O !`)
}
}
// 获取所有sql脚本内容
let sqlContentMap = getSqlContentMap()
// 执行建表sql脚本
const createAllTables = async () => {
for (let key in sqlContentMap) {
let sqlShell = sqlContentMap[key]
let sqlShellList = sqlShell.split(';')
for (let [i, shell] of sqlShellList.entries()) {
if (shell.trim()) {
let result = await query(shell)
if (result.serverStatus * 1 === 2) {
eventLog(null, key, i)
} else {
eventLog(true, key, i)
}
}
}
}
console.log('sql脚本执行结束!')
console.log('请按 ctrl + c 键退出!')
}
createAllTables()

11
init/save/open.sql

@ -0,0 +1,11 @@
-- CREATE TABLE IF NOT EXISTS `open` (
-- `id` int(11) NOT NULL AUTO_INCREMENT,
-- `card` varchar(255) NOT NULL,
-- `host` varchar(255) NOT NULL,
-- PRIMARY KEY (`id`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- INSERT INTO `open` set card='[0,0,0,1,1]', host='1';
-- INSERT INTO `open` set card='[0,0,0,1,2]', host='2';

13
init/sql/category.sql

@ -0,0 +1,13 @@
CREATE TABLE IF NOT EXISTS `category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`belongto` varchar(255) DEFAULT NULL,
`about` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `category` set id='1', name='html', belongto='0', about='22';
INSERT INTO `category` set id='2', name='css', belongto='0', about='2256';
INSERT INTO `category` set id='3', name='js', belongto='1', about='5422';

14
init/sql/user.sql

@ -0,0 +1,14 @@
-- level: 0:管理员
-- level: 1:普通用户
CREATE TABLE IF NOT EXISTS `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`imgurl` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`nickname` varchar(255) DEFAULT NULL,
`createTime` varchar(20) DEFAULT NULL,
`modifiedTime` varchar(20) DEFAULT NULL,
`level` int(11) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

30
init/util/db.js

@ -0,0 +1,30 @@
const mysql = require('mysql')
var config = require('../../config.js')
const pool = mysql.createPool({
...config.db
})
let query = function (sql, values) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err)
} else {
connection.query(sql, values, (err, rows) => {
if (err) {
reject(err)
} else {
resolve(rows)
}
connection.release()
})
}
})
})
}
module.exports = {
query
}

30
init/util/get-sql-content-map.js

@ -0,0 +1,30 @@
const fs = require('fs')
const getSqlMap = require('./get-sql-map')
let sqlContentMap = {}
/**
* 读取sql文件内容
* @param {string} fileName 文件名称
* @param {string} path 文件所在的路径
* @return {string} 脚本文件内容
*/
function getSqlContent( fileName, path ) {
let content = fs.readFileSync( path, 'binary' )
sqlContentMap[ fileName ] = content
}
/**
* 封装所有sql文件脚本内容
* @return {object}
*/
function getSqlContentMap () {
let sqlMap = getSqlMap()
for( let key in sqlMap ) {
getSqlContent( key, sqlMap[key] )
}
return sqlContentMap
}
module.exports = getSqlContentMap

20
init/util/get-sql-map.js

@ -0,0 +1,20 @@
const fs = require('fs')
const walkFile = require('./walk-file')
/**
* 获取sql目录下的文件目录数据
* @return {object}
*/
function getSqlMap() {
let basePath = __dirname
basePath = basePath.replace(/\\/g, '\/')
let pathArr = basePath.split('\/')
pathArr = pathArr.splice(0, pathArr.length - 1)
basePath = pathArr.join('/') + '/sql/'
let fileList = walkFile(basePath, 'sql')
return fileList
}
module.exports = getSqlMap

28
init/util/walk-file.js

@ -0,0 +1,28 @@
const fs = require('fs')
/**
* 遍历目录下的文件目录
* @param {string} pathResolve 需进行遍历的目录路径
* @param {string} mime 遍历文件的后缀名
* @return {object} 返回遍历后的目录结果
*/
const walkFile = function( pathResolve , mime ){
let files = fs.readdirSync( pathResolve )
let fileList = {}
for( let [ i, item] of files.entries() ) {
let itemArr = item.split('\.')
let itemMime = ( itemArr.length > 1 ) ? itemArr[ itemArr.length - 1 ] : 'undefined'
let keyName = item + ''
if( mime === itemMime ) {
fileList[ item ] = pathResolve + item
}
}
return fileList
}
module.exports = walkFile

98
models/menus.js

@ -0,0 +1,98 @@
const dbUtils = require('../utils/db-util')
const menus = {
async ceateMenu(model) {
let result = await dbUtils.insertData('category', model);
return result;
},
// async deleteMenu(id) {
// let result = await dbUtils.deleteDataById('category', id);
// if (result.serverStatus == 2) {
// result = true
// } else {
// result = false
// }
// },
deleteMenu(id,subList,fix) {
let sql = [];
sql.push('DELETE FROM category WHERE id = '+id);
if(subList){
for (let i = 0; i < subList.length; i++) {
sql.push("UPDATE category SET belongto='"+fix+"' WHERE id = "+subList[i].id);
}
}
console.log(sql)
return new Promise((resolve,reject)=>{
dbUtils.execTrans(sql,(err)=>{
if (err){
resolve(false);
} else {
resolve(true)
}
})
})
},
/**
* 更新菜单信息
* @param {object} model 数据模型
* @return {object} mysql执行结果
*/
async update(model, id) {
let result = await dbUtils.updateData('category', model, id)
if (result.serverStatus == 2) {
result = true
} else {
result = false
}
return result
},
/**
* 获取所有菜单
*/
async getAllMenus() {
let _sql = `SELECT * from category;`;
let result = await dbUtils.query(_sql);
if (Array.isArray(result)) {
result = result
} else {
result = null;
}
return result
},
async getMenusByID(id) {
let _sql = `
SELECT * from category
where id="${id}";`;
let result = await dbUtils.query(_sql);
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
/**
* 获取子菜单
* 0 主菜单
* 1 属于ID:1的子菜单
* 2 属于ID:2的子菜单
*/
async getMenusByBelongTo(belongto) {
let _sql = `
SELECT * from category
where belongto="${belongto}";`;
let result = await dbUtils.query(_sql);
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
}
module.exports = menus

110
models/user-info.js

@ -0,0 +1,110 @@
const dbUtils = require('./../utils/db-util')
const user = {
/**
* 数据库创建用户
* @param {object} model 用户数据模型
* @return {object} mysql执行结果
*/
async create(model) {
let result = await dbUtils.insertData('user_info', model)
return result
},
/**
* 更新用户信息
* @param {object} model 用户数据模型
* @return {object} mysql执行结果
*/
async update(model, id) {
let result = await dbUtils.updateData('user_info', model, id)
if (result.serverStatus == 2) {
result = true
} else {
result = false
}
return result
},
/**
* 查找一个存在用户的数据
* @param {obejct} options 查找条件参数
* @return {object|null} 查找结果
*/
async getExistOne(options) {
let _sql = `
SELECT * from user_info
where email="${options.email}" or username="${options.username}";`
let result = await dbUtils.query(_sql)
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
/**
* 根据ID或者用户名查找一个存在用户的数据
* @param {obejct} options 查找条件参数
* @return {object|null} 查找结果
*/
async getExistOneByIdOrUserName(options) {
let _sql = '';
if (options.username) {
_sql = `
SELECT * from user_info
where username="${options.username}";`
}
if (options.id) {
_sql = `
SELECT * from user_info
where id="${options.id}";`
}
let result = await dbUtils.query(_sql)
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
/**
* 根据用户名和密码查找用户
* @param {object} options 用户名密码对象
* @return {object|null} 查找结果
*/
async getOneByUserName(options) {
let _sql = `
SELECT * from user_info
where username="${options.username}";`
let result = await dbUtils.query(_sql)
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
/**
* 根据用户名查找用户信息
* @param {string} userName 用户账号名称
* @return {object|null} 查找结果
*/
async getUserInfoByUserName(userName) {
let _sql = `
SELECT * from user_info
where username="${userName}";`;
let result = await dbUtils.query(_sql);
if (Array.isArray(result) && result.length > 0) {
result = result
} else {
result = null;
}
return result
},
}
module.exports = user

4234
package-lock.json

File diff suppressed because it is too large

38
package.json

@ -0,0 +1,38 @@
{
"name": "noderest",
"version": "1.0.0",
"description": "",
"main": "./src/main.js",
"private": true,
"scripts": {
"dev": "set DEBUG=demo:server,demo:ssc,demo:sql & cross-env PORT=3303 nodemon ./bin/www",
"db": "node ./init/index.js",
"test": "mocha"
},
"keywords": [],
"author": "Dash Crash",
"license": "ISC",
"dependencies": {
"@koa/cors": "^2.2.3",
"async": "^3.1.0",
"bcryptjs": "^2.4.3",
"debug": "^4.1.1",
"jsonwebtoken": "^8.5.1",
"koa": "^2.11.0",
"koa-body": "^4.1.1",
"koa-jwt": "^3.6.0",
"koa-router": "^7.4.0",
"koa-static": "^5.0.0",
"lodash": "^4.17.15",
"mocha": "^6.2.2",
"module-alias": "^2.2.2",
"mysql": "^2.17.1",
"nodemon": "^1.19.4",
"pug": "^2.0.4",
"validator": "^12.0.0"
},
"devDependencies": {
"cross-env": "^6.0.3",
"koa-views": "^6.2.1"
}
}

9
public/home.pug

@ -0,0 +1,9 @@
extends template/layout.pug
block content
include ui/login.pug
append script
//script init({nb: decode("#{data}")})
script(src='/static/js/home.js');

3
public/static/css/index.css

@ -0,0 +1,3 @@
body {
background-color: #F5F5D5;
}

10
public/static/css/style.css

@ -0,0 +1,10 @@
/**************************************/
.login-panel .mask{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.2);
}

1
public/static/js/home.js

@ -0,0 +1 @@
const homeVue = init({nb: decode("#{data}")})

17
public/static/js/index.js

@ -0,0 +1,17 @@
const init = (data = {}) => {
return new Vue.extend({
data() {
return data
}
})
}
const decode = (text) => {
var temp = document.createElement("div");
//2.然后将要转换的字符串设置为这个元素的innerHTML(ie,火狐,google都支持)
temp.innerHTML = text;
//3.最后返回这个元素的innerText(ie支持)或者textContent(火狐,google支持),即得到经过HTML解码的字符串了。
var output = temp.innerText || temp.textContent;
temp = null;
return output
}

1
public/template/footer.pug

@ -0,0 +1 @@
footer

1
public/template/header.pug

@ -0,0 +1 @@
header

19
public/template/layout.pug

@ -0,0 +1,19 @@
//- https://pug.bootcss.com/language/inheritance.html
html
head
title 我的站点
block css
link(href='/static/css/index.css' rel='stylesheet')
link(href='/static/css/style.css' rel='stylesheet')
body
#app
block header
include header.pug
block content
block foot
include footer.pug
block script
script(src='https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js')
script(src='https://cdn.jsdelivr.net/npm/vue/dist/vue.js')
script(src='/static/js/index.js')

3
public/ui/login.pug

@ -0,0 +1,3 @@
.login-panel
.mask
div {{nb}}

13
readme.md

@ -0,0 +1,13 @@
## 约定
```
//各个模块之间的消息传递
//200表示正确的返回
//其他表示处理中存在错误
{
code: 200,
data: null,
message: 'ok'
}
```
todo:将数据库操作全部改为占位符形式防止sql注入

14
routes/api/menus.js

@ -0,0 +1,14 @@
var Router = require('koa-router');
const controler = require('../../controler/index.js')
var router = new Router();
router.post('/menus/getall', controler.menus.getAll);
router.post('/menus/create', controler.menus.createOne);
router.post('/menus/update', controler.menus.updateOne);
router.post('/menus/delete', controler.menus.deleteOne);
module.exports = {
name: __dirname.split('\\')[__dirname.split('\\').length - 1],
router
};

14
routes/api/user.js

@ -0,0 +1,14 @@
var Router = require('koa-router');
const controler = require('../../controler/index.js')
var router = new Router();
router.post('/login', controler.user.login);
router.post('/register', controler.user.register);
router.get('/userinfo', controler.user.getUserinfo);
router.post('/modifyuserinfo', controler.user.modifyUserinfo);
module.exports = {
name: __dirname.split('\\')[__dirname.split('\\').length - 1],
router
};

34
routes/index.js

@ -0,0 +1,34 @@
const path = require('path');
const Bean = require('../utils/index.js');
const router = require('koa-router')();
// 路由收集规则
const api_routes = Bean.readFileDeep(path.resolve(__dirname, 'api')).file;
const api_root = '/api';
api_routes.forEach(route => {
if (route != path.resolve(__filename)) {
const obj = require(route);
const child_router = obj.router;
// 可以根据路由筛选动态设置路由前缀
// const name = obj.name;
// router.prefix('/api');
// app.use(router.routes()).use(router.allowedMethods());
router.use(api_root, child_router.routes(), child_router.allowedMethods())
}
});
const views_routes = Bean.readFileDeep(path.resolve(__dirname, 'views')).file;
const views_root = '/';
views_routes.forEach(route => {
if (route != path.resolve(__filename)) {
const obj = require(route);
const child_router = obj.router;
// 可以根据路由筛选动态设置路由前缀
// const name = obj.name;
// router.prefix('/api');
// app.use(router.routes()).use(router.allowedMethods());
router.use(views_root, child_router.routes(), child_router.allowedMethods())
}
});
module.exports = router

12
routes/views/home.js

@ -0,0 +1,12 @@
var Router = require('koa-router');
const controler = require('../../controler/index.js')
var router = new Router();
router.get('/', controler.views.home);
module.exports = {
name: __dirname.split('\\')[__dirname.split('\\').length - 1],
router
};

84
services/menus.js

@ -0,0 +1,84 @@
const menus = require('./../models/menus');
//菜单的增删改查
const menuServices = {
// 获取所有菜单
async getAllMenus() {
let result = await menus.getAllMenus();
return result
},
async createMenu(model) {
let result = await menus.ceateMenu(model);
return result
},
async judgeID(id) {
let result = await menus.getMenusByID(id);
if (result){
result = result[0];
let belong = await menus.getMenusByBelongTo(result.id);
if (belong){
if (belong.length>0){
return belong;
} else {
return null;
}
}else {
return null;
}
}else {
return null;
}
},
async deleteMenu(model,list,fix) {
let id = model.id;
let result = await menus.deleteMenu(id,list,fix);
return result
},
async updateMenu(model) {
let data = {
...model,
// modifiedTime: new Date().getTime()
}
let id = data.id;
delete data.id;
let result = await menus.update(data,id);
return result
},
/**
* 验证所需要的参数
* @param model
* @returns {Promise<void>}
*/
async validatorNeed(model,keys=[]) {
let result = {
success: true,
message: '',
data: null
}
for (let key of keys){
let notHaveValue = model[key.key]==undefined
if (notHaveValue){
result.success=false;
result.message=key.error;
break;
}
let notJudge = key.validate&&await key.validate(model[key.key]);
if (typeof notJudge == 'string'){
result.success=false;
result.message=notJudge;
break;
}else if (typeof notJudge == 'boolean'&&notJudge){
result.success=false;
result.message=key.error;
break;
}
}
return result;
}
}
module.exports = menuServices;

176
services/user-info.js

@ -0,0 +1,176 @@
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const {
promisify
} = require('util');
/**
* 用户业务操作
*/
const config = require('./../config.js');
const validator = require('validator')
const userModel = require('./../models/user-info')
const userCode = require('./../codes/user')
const user = {
/**
* 创建用户
* @param {object} user 用户信息
* @return {object} 创建结果
*/
async create(user) {
let salt = await promisify(bcrypt.genSalt)(10);
let pwd = await promisify(bcrypt.hash)(user.password, salt);
user.password = pwd;
let result = await userModel.create(user)
return result
},
/**
* 更新用户
* @param {object} user 用户信息
* @return {object} 创建结果
*/
async update(user, id) {
let data = {
...user,
modifiedTime: new Date().getTime()
}
// /不能修改用户名
delete data.username;
// 不能修改邮箱
delete data.email;
let resultData = await userModel.update(data, id)
return resultData
},
/**
* 查找存在用户信息
* @param {object} formData 查找的表单数据
* @return {object|null} 查找结果
*/
async getExistOne(formData) {
let resultData = await userModel.getExistOne({
'email': formData.email,
'username': formData.username
})
return resultData
},
/**
* 根据ID查找存在用户信息
* @param {object} formData 查找的表单数据
* @return {object|null} 查找结果
*/
async getExistOneByIdOrUserName(formData) {
let resultData = await userModel.getExistOneByIdOrUserName({
'id': formData.id
})
if (resultData) {
return resultData[0];
}
return resultData
},
/**
* 登录Token获取
* @param {object} formData 登录表单信息
* @return {object} 登录业务操作结果
*/
async validatorPwd(formData, rowData) {
let token = '';
let isSame = await promisify(bcrypt.compare)(formData.password, rowData.password);
if (isSame) {
// 1小时的登录时间
token = jwt.sign({
id: rowData.id,
username: rowData.username,
}, config.share_key, {
expiresIn: '1h'
});
token = 'Bearer ' + token;
}
return token
},
/**
* 根据用户名查找用户业务操作
* @param {string} userName 用户名
* @return {object|null} 查找结果
*/
async getUserInfoByUserName(userName) {
let resultData = await userModel.getUserInfoByUserName(userName) || {}
// let userInfo = {
// // id: resultData.id,
// email: resultData.email,
// username: resultData.username,
// detailInfo: resultData.detail_info,
// createTime: resultData.create_time
// }
return resultData
},
/**
* 检验用户注册数据
* @param {object} userInfo 用户注册数据
* @return {object} 校验结果
*/
validatorSignUp(userInfo) {
let result = {
success: false,
message: '',
data: null
}
if (/^[a-z0-9\_\-]{6,16}$/.test(userInfo.username || '') === false) {
result.message = userCode.ERROR_USER_NAME
return result
}
if (userInfo.email && !validator.isEmail(userInfo.email)) {
;
result.message = userCode.ERROR_EMAIL
return result
}
if (!/[\w+]{6,16}/.test(userInfo.password || '')) {
result.message = userCode.ERROR_PASSWORD
return result
}
if (userInfo.password !== userInfo.confirmPassword) {
result.message = userCode.ERROR_PASSWORD_CONFORM
return result
}
result.success = true
return result
},
/**
* 检验用户必须的数据
* @param {object} userInfo 用户数据
* @return {object} 校验结果
*/
validatorNeed(userInfo, needPwd = true) {
let result = {
success: false,
message: '',
}
if (!userInfo.username && !userInfo.email) {
result.success = false;
result.message = '用户名或者邮箱不能为空'
return result
}
if (userInfo.email && !validator.isEmail(userInfo.email)) {
result.success = false;
result.message = userCode.ERROR_EMAIL
return result
}
if (needPwd && !userInfo.password) {
result.success = false;
result.message = '密码不能为空'
return result
}
result.success = true
return result
}
}
module.exports = user

0
test/host.spec.js

14
test/user.spec.js

@ -0,0 +1,14 @@
const assert = require('assert');
const userdb = require('./../src/models/user-info.js');
describe('#hello.js', () => {
it('0', async (done) => {
try {
let data = userdb.create({
username: "sdada"
});
done();
} catch (error) {
done(error);
}
});
});

66
utils/datetime.js

@ -0,0 +1,66 @@
const monthEnum = [
'01', '02', '03', '04', '05', '06',
'07', '08', '09', '10', '11', '12',
]
const dayEnum = [
'01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '04', '25', '26', '27', '28', '29', '30', '31',
]
const timeEnum = [
'00',
'01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '04', '25', '26', '27', '28', '29', '30',
'31', '32', '33', '34', '35', '36', '37', '38', '39', '40',
'41', '42', '43', '44', '45', '46', '47', '48', '49', '50',
'51', '52', '53', '54', '55', '56', '57', '58', '59',
]
const datatime = {
parseStampToFormat(timestamp, type) {
let _date
if (timestamp) {
_date = new Date(timestamp)
} else {
_date = new Date()
}
let parsedDate
let parseTime
let parseDatetime
let yearNum = _date.getFullYear()
let monthNum = monthEnum[_date.getMonth()]
let dayNum = dayEnum[_date.getDate() - 1]
let hourNum = timeEnum[_date.getHours()]
let minNum = timeEnum[_date.getMinutes()]
let secNum = timeEnum[_date.getSeconds()]
type = type || 'YYYY/MM/DD/hh/mm/ss'
parseDatetime = type
.replace('YYYY', yearNum)
.replace('MM', monthNum)
.replace('DD', dayNum)
.replace('hh', hourNum)
.replace('mm', minNum)
.replace('ss', secNum)
return parseDatetime
},
getNowDatetime() {
let timestamp = new Date().getTime()
let nowDatetime = this.parseStampToFormat(timestamp)
return nowDatetime
},
}
module.exports = datatime

162
utils/db-util.js

@ -0,0 +1,162 @@
var config = require('./../config.js')
const mysql = require("mysql")
const async = require("async");
const pool = mysql.createPool({
...config.db
})
// var pool = mysql.createPool({
// host: "144.48.4.186",
// user: "noderest",
// password: "8WBtsejYGiEESJMh",
// database: "noderest",
// connectionLimit: 33,
// port: "3306",
// waitForConnections: false
// });
//mysql事务处理
let execTrans =function(sqlparamsEntities, callback) {
//建立连接
pool.getConnection(function (err, connection) {
if (err) {
//抛出连接错误
return callback(err, null);
}
connection.beginTransaction(function (err) {
if (err) {
return callback(err, null);
}
console.log("开始执行transaction,共执行" + sqlparamsEntities.length + "条数据");
var funcAry = [];
sqlparamsEntities.forEach(function (sql_param) {
var temp = function (cb) {
// var sql = sql_param.sql;
// var param = sql_param.params;
connection.query(sql_param, function (tErr, rows, fields) {
if (tErr) {
connection.rollback(function () {
console.log("事务失败," + sql_param + ",ERROR:" + tErr);
throw tErr;
});
} else {
return cb(null, 'ok');
}
})
};
funcAry.push(temp);
});
async.series(funcAry, function (err, result) {
console.log("transaction error: " + err);
if (err) {
connection.rollback(function (err) {
console.log("transaction error: " + err);
connection.release();
return callback(err, null);
});
} else {
connection.commit(function (err, info) {
console.log("transaction info: " + JSON.stringify(info));
if (err) {
console.log("执行事务失败," + err);
connection.rollback(function (err) {
console.log("transaction error: " + err);
connection.release();
return callback(err, null);
});
} else {
connection.release();
return callback(null, info);
}
})
}
})
});
});
}
let query = function (sql, values) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
resolve(err)
} else {
connection.query(sql, values, (err, rows) => {
if (err) {
reject(err)
} else {
resolve(rows)
}
connection.release()
})
}
})
})
}
let createTable = function (sql) {
return query(sql, [])
}
let findDataLast = function (table) {
let _sql = "SELECT * FROM ?? ORDER BY id DESC LIMIT 1;"
return query(_sql, [table])
}
let findDataById = function (table, id) {
let _sql = "SELECT * FROM ?? WHERE id = ? "
return query(_sql, [table, id, start, end])
}
let findDataByPage = function (table, keys, start, end) {
let _sql = "SELECT ?? FROM ?? LIMIT ? , ?"
return query(_sql, [keys, table, start, end])
}
let insertData = function (table, values) {
let _sql = "INSERT INTO ?? SET ?"
return query(_sql, [table, values])
}
let updateData = function (table, values, id) {
let _sql = "UPDATE ?? SET ? WHERE id = ?"
return query(_sql, [table, values, id])
}
let deleteDataById = function (table, id) {
let _sql = "DELETE FROM ?? WHERE id = ?"
return query(_sql, [table, id])
}
let select = function (table, keys) {
let _sql = "SELECT ?? FROM ?? "
return query(_sql, [keys, table])
}
let count = function (table) {
let _sql = "SELECT COUNT(*) AS total_count FROM ?? "
return query(_sql, [table])
}
module.exports = {
query,
findDataLast,
createTable,
findDataById,
findDataByPage,
deleteDataById,
insertData,
updateData,
select,
count,
execTrans,
}

26
utils/index.js

@ -0,0 +1,26 @@
const fs = require('fs');
const path = require('path');
module.exports = {
readFileDeep(dir) {
const routes = {
dir: [],
file: []
};
const routeFunc = (dirname) => {
const files = fs.readdirSync(path.resolve(dirname));
files.forEach(file => {
if (fs.statSync(path.resolve(dirname, file)).isDirectory()) {
routes.dir.push(path.resolve(dirname, file));
routeFunc(path.resolve(dirname, file))
}
if (fs.statSync(path.resolve(dirname, file)).isFile()) {
routes.file.push(path.resolve(dirname, file));
}
})
return routes
}
return routeFunc(dir);
}
}

37
utils/type.js

@ -0,0 +1,37 @@
const Types = {
isPrototype(data) {
return Object.prototype.toString.call(data).toLowerCase()
},
isArray(data) {
return this.isPrototype(data) === '[object array]'
},
isJSON(data) {
return this.isPrototype(data) === '[object object]'
},
isFunction(data) {
return this.isPrototype(data) === '[object function]'
},
isString(data) {
return this.isPrototype(data) === '[object string]'
},
isNumber(data) {
return this.isPrototype(data) === '[object number]'
},
isUndefined(data) {
return this.isPrototype(data) === '[object undefined]'
},
isNull(data) {
return this.isPrototype(data) === '[object null]'
}
}
module.exports = Types
Loading…
Cancel
Save