[init] server build up
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}]
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
dist
|
||||
node_modules
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
npm-debug.log
|
||||
.git
|
||||
.hg
|
||||
.svn
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,3 @@
|
||||
build/*.js
|
||||
assets/*.js
|
||||
test/**/*.js
|
||||
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
ecmaVersion: 2017, //指定ECMAScript支持的版本,6为ES6,这里为了兼容async和await,设置为2017
|
||||
sourceType: 'module'
|
||||
},
|
||||
extends: 'standard',
|
||||
plugins: [
|
||||
'html',
|
||||
'promise'
|
||||
],
|
||||
env: {
|
||||
'node': true
|
||||
},
|
||||
rules: {
|
||||
// allow console
|
||||
'no-console': 0,
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
// allow async-await
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': 0
|
||||
},
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log
|
||||
test/unit/coverage
|
||||
test/e2e/reports
|
||||
selenium-debug.log
|
||||
.idea/
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"indent_size": 2,
|
||||
"indent_char": " ",
|
||||
"other": " ",
|
||||
"indent_level": 0,
|
||||
"indent_with_tabs": false,
|
||||
"preserve_newlines": true,
|
||||
"max_preserve_newlines": 2,
|
||||
"jslint_happy": true,
|
||||
"indent_handlebars": true
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
FROM node:10-alpine
|
||||
|
||||
ENV EXPOSE_PORT 3000
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install -g @babel/cli @babel/core \
|
||||
&& npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE ${EXPOSE_PORT}
|
||||
|
||||
ENTRYPOINT [ "node", "/app/dist/app.js" ]
|
||||
@@ -0,0 +1,11 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,2 @@
|
||||
require('@babel/register')
|
||||
require('../src/app')
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
const gulp = require('gulp')
|
||||
const eslint = require('gulp-eslint')
|
||||
const nodemon = require('gulp-nodemon')
|
||||
const friendlyFormatter = require('eslint-friendly-formatter')
|
||||
|
||||
var jsScript = 'node'
|
||||
if (process.env.npm_config_argv !== undefined && process.env.npm_config_argv.indexOf('debug') > 0) {
|
||||
jsScript = 'node debug'
|
||||
}
|
||||
|
||||
function lintOne (aims) {
|
||||
console.log('ESlint:' + aims)
|
||||
console.time('Finished eslint')
|
||||
return gulp.src(aims)
|
||||
.pipe(eslint({ configFile: './.eslintrc.js' }))
|
||||
.pipe(eslint.format(friendlyFormatter))
|
||||
.pipe(eslint.results(results => {
|
||||
// Called once for all ESLint results.
|
||||
console.log(`- Total Results: ${results.length}`)
|
||||
console.log(`- Total Warnings: ${results.warningCount}`)
|
||||
console.log(`- Total Errors: ${results.errorCount}`)
|
||||
console.timeEnd('Finished eslint')
|
||||
}))
|
||||
}
|
||||
|
||||
gulp.task('ESlint', () => {
|
||||
return gulp.src(['src/**/*.js', '!node_modules/**'])
|
||||
.pipe(eslint({ configFile: './.eslintrc.js' }))
|
||||
.pipe(eslint.format(friendlyFormatter))
|
||||
// .pipe(eslint.failAfterError())
|
||||
.pipe(eslint.results(results => {
|
||||
// Called once for all ESLint results.
|
||||
console.log(`- Total Results: ${results.length}`)
|
||||
console.log(`- Total Warnings: ${results.warningCount}`)
|
||||
console.log(`- Total Errors: ${results.errorCount}`)
|
||||
}))
|
||||
})
|
||||
|
||||
gulp.task('ESlint_nodemon', gulp.series('ESlint', function () {
|
||||
var stream = nodemon({
|
||||
script: 'build/dev-server.js',
|
||||
execMap: {
|
||||
js: jsScript
|
||||
},
|
||||
tasks: function (changedFiles) {
|
||||
lintOne(changedFiles)
|
||||
return []
|
||||
},
|
||||
verbose: true,
|
||||
ignore: ['build/*.js', 'dist/*.js', 'nodemon.json', '.git', 'node_modules/**/node_modules', 'gulpfile.js'],
|
||||
env: {
|
||||
NODE_ENV: 'development'
|
||||
},
|
||||
ext: 'js json'
|
||||
})
|
||||
|
||||
return stream
|
||||
.on('restart', function () {
|
||||
// console.log('Application has restarted!')
|
||||
})
|
||||
.on('crash', function () {
|
||||
console.error('Application has crashed!\n')
|
||||
// stream.emit('restart', 20) // restart the server in 20 seconds
|
||||
})
|
||||
}))
|
||||
|
||||
gulp.task('nodemon', function () {
|
||||
return nodemon({
|
||||
script: 'build/dev-server.js',
|
||||
execMap: {
|
||||
js: jsScript
|
||||
},
|
||||
verbose: true,
|
||||
ignore: ['build/*.js', 'dist/*.js', 'nodemon.json', '.git', 'node_modules/**/node_modules', 'gulpfile.js'],
|
||||
env: {
|
||||
NODE_ENV: 'development'
|
||||
},
|
||||
ext: 'js json'
|
||||
})
|
||||
})
|
||||
|
||||
gulp.task('default', gulp.series('ESlint', 'ESlint_nodemon', function () {
|
||||
// console.log('ESlin检查完成')
|
||||
}))
|
||||
@@ -0,0 +1 @@
|
||||
ok
|
||||
Generated
+12100
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "koa2-api-scaffold",
|
||||
"version": "1.0.4",
|
||||
"description": "Koa2 RESTful API 服务器的脚手架",
|
||||
"author": "yi-ge <a@wyr.me>",
|
||||
"license": "WTFPL",
|
||||
"scripts": {
|
||||
"start": "gulp nodemon",
|
||||
"dev": "gulp",
|
||||
"build": "babel src -d dist",
|
||||
"production": "node dist/app.js",
|
||||
"test": "jest",
|
||||
"link": "eslint .",
|
||||
"link:fix": "eslint --fix ."
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"koa": "^2.11.0",
|
||||
"koa-body": "^4.1.1",
|
||||
"koa-compose": "^4.1.0",
|
||||
"koa-cors": "0.0.16",
|
||||
"koa-jwt": "^3.6.0",
|
||||
"koa-router": "^8.0.8",
|
||||
"koa-static2": "^0.1.8",
|
||||
"nodemailer": "^6.4.6",
|
||||
"pg": "^8.3.3",
|
||||
"pg-hstore": "^2.3.3",
|
||||
"promise-mysql": "^4.1.3",
|
||||
"sequelize": "^5.22.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/plugin-external-helpers": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@babel/register": "^7.9.0",
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^25.3.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-plugin-html": "^6.0.1",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-jest": "^23.8.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-eslint": "^6.0.0",
|
||||
"gulp-nodemon": "^2.5.0",
|
||||
"jest": "^25.3.0",
|
||||
"koa-logger": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 7.8.0",
|
||||
"npm": ">= 4.2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'RESRful API Server',
|
||||
script: './dist/app.js',
|
||||
watch: false, // 默认关闭watch 可替换为 ['src']
|
||||
ignore_watch: ['node_modules', 'build', 'logs'],
|
||||
out_file: '/logs/out.log', // 日志输出
|
||||
error_file: '/logs/error.log', // 错误日志
|
||||
max_memory_restart: '2G', // 超过多大内存自动重启,仅防止内存泄露有意义,需要根据自己的业务设置
|
||||
env: {
|
||||
NODE_ENV: 'production'
|
||||
},
|
||||
exec_mode: 'cluster', // 开启多线程模式,用于负载均衡
|
||||
instances: 'max', // 启用多少个实例,可用于负载均衡
|
||||
autorestart: true // 程序崩溃后自动重启
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
wqdjkwl1e21FQlk1j2
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
import Koa2 from 'koa'
|
||||
import KoaBody from 'koa-body'
|
||||
// import cors from 'koa-cors'
|
||||
import KoaStatic from 'koa-static2'
|
||||
import {
|
||||
System as SystemConfig
|
||||
} from './config'
|
||||
import path from 'path'
|
||||
import MainRoutes from './routes/main-routes'
|
||||
import ErrorRoutesCatch from './middleware/ErrorRoutesCatch'
|
||||
import ErrorRoutes from './routes/error-routes'
|
||||
import jwt from 'koa-jwt'
|
||||
import fs from 'fs'
|
||||
import db from './lib/sequelize'
|
||||
|
||||
const app = new Koa2()
|
||||
const env = process.env.NODE_ENV || 'development' // Current mode
|
||||
|
||||
const publicKey = fs.readFileSync(path.join(__dirname, '../publicKey.pub'))
|
||||
|
||||
app
|
||||
.use((ctx, next) => {
|
||||
if (ctx.request.header.host.split(':')[0] === 'localhost' || ctx.request.header.host.split(':')[0] === '127.0.0.1') {
|
||||
ctx.set('Access-Control-Allow-Origin', '*')
|
||||
} else {
|
||||
ctx.set('Access-Control-Allow-Origin', SystemConfig.HTTP_server_host)
|
||||
}
|
||||
ctx.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
||||
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
|
||||
ctx.set('Access-Control-Allow-Credentials', true) // 允许带上 cookie
|
||||
return next()
|
||||
})
|
||||
// .use(cors)
|
||||
.use(ErrorRoutesCatch())
|
||||
.use(KoaStatic('assets', path.resolve(__dirname, '../assets'))) // Static resource
|
||||
.use(jwt({ secret: publicKey }).unless({ path: [/^\/public|\/user\/login|\/assets/] }))
|
||||
.use(KoaBody({
|
||||
multipart: true,
|
||||
parsedMethods: ['POST', 'PUT', 'PATCH', 'GET', 'HEAD', 'DELETE'], // parse GET, HEAD, DELETE requests
|
||||
formidable: {
|
||||
uploadDir: path.join(__dirname, '../assets/uploads/tmp')
|
||||
},
|
||||
jsonLimit: '10mb',
|
||||
formLimit: '10mb',
|
||||
textLimit: '10mb'
|
||||
})) // Processing request
|
||||
// .use(PluginLoader(SystemConfig.System_plugin_path))
|
||||
.use(MainRoutes.routes())
|
||||
.use(MainRoutes.allowedMethods())
|
||||
.use(ErrorRoutes())
|
||||
|
||||
if (env === 'development') { // logger
|
||||
app.use((ctx, next) => {
|
||||
const start = new Date()
|
||||
return next().then(() => {
|
||||
const ms = new Date() - start
|
||||
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
app.listen(SystemConfig.API_server_port)
|
||||
|
||||
console.log('Now start API server on port ' + SystemConfig.API_server_port + '...')
|
||||
|
||||
db.sequelize.authenticate()
|
||||
.then(() => {
|
||||
console.log('資料庫連接成功')
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`資料庫連接錯誤:${error}`)
|
||||
})
|
||||
|
||||
db.sequelize.sync()
|
||||
|
||||
export default app
|
||||
@@ -0,0 +1,39 @@
|
||||
import path from 'path'
|
||||
|
||||
// 系统配置
|
||||
export const System = {
|
||||
API_server_type: 'http://', // API服务器协议类型,包含"http://"或"https://"
|
||||
API_server_host: 'localhost', // API服务器暴露的域名地址,请勿添加"http://"
|
||||
API_server_port: '3000', // API服务器监听的端口号
|
||||
HTTP_server_type: 'http://', // HTTP服务器协议类型,包含"http://"或"https://"
|
||||
HTTP_server_host: 'www.XXX.com', // HTTP服务器地址,请勿添加"http://" (即前端调用使用的服务器地址,如果是APP请设置为 * )
|
||||
HTTP_server_port: '65534', // HTTP服务器端口号
|
||||
System_country: 'zh-cn', // 所在国家的国家代码
|
||||
System_plugin_path: path.join(__dirname, './plugins'), // 插件路径
|
||||
Session_Key: 'RESTfulAPI', // 生产环境务必随机设置一个值
|
||||
db_type: 'mysql' // 数据库类型
|
||||
}
|
||||
|
||||
export const DB = {
|
||||
host: 'localhost', // 服务器地址
|
||||
port: 3306, // 数据库端口号
|
||||
username: 'admin', // 数据库用户名
|
||||
password: 'admin888', // 数据库密码
|
||||
database: 'development', // 数据库名称
|
||||
prefix: 'api_' // 默认"api_"
|
||||
}
|
||||
|
||||
export const pgDB = {
|
||||
host: '127.0.0.1', // 服务器地址
|
||||
port: 54321, // 数据库端口号
|
||||
username: 'biopro', // 数据库用户名
|
||||
password: 'BioProControlBox', // 数据库密码
|
||||
database: 'postgres' // 数据库名称
|
||||
}
|
||||
|
||||
export const SendEmail = {
|
||||
service: 'smtp.abcd.com', // SMTP服务提供商域名
|
||||
username: 'postmaster%40abcd.com', // 用户名/用户邮箱
|
||||
password: 'password', // 邮箱密码
|
||||
sender_address: '"XX平台 👥" <postmaster@abcd.com>'
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export const Get = async (ctx, next) => {
|
||||
ctx.body = {
|
||||
result: 'get',
|
||||
name: ctx.params.name,
|
||||
para: ctx.query
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const Post = async (ctx, next) => {
|
||||
ctx.body = {
|
||||
result: 'post',
|
||||
name: ctx.params.name,
|
||||
para: ctx.request.body
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const Put = (ctx, next) => {
|
||||
ctx.body = {
|
||||
result: 'put',
|
||||
name: ctx.params.name,
|
||||
para: ctx.request.body
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const Delete = (ctx, next) => {
|
||||
ctx.body = {
|
||||
result: 'delete',
|
||||
name: ctx.params.name,
|
||||
para: ctx.request.body
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const publicKey = fs.readFileSync(path.join(__dirname, '../../publicKey.pub'))
|
||||
|
||||
// 用户登录的时候返回token
|
||||
// let token = jwt.sign({
|
||||
// userInfo: userInfo // 你要保存到token的数据
|
||||
// }, publicKey, { expiresIn: '7d' })
|
||||
|
||||
/**
|
||||
* 检查授权是否合法
|
||||
*/
|
||||
const CheckAuth = (ctx, next) => {
|
||||
const token = ctx.request.header.authorization
|
||||
try {
|
||||
const decoded = jwt.verify(token.substr(7), publicKey)
|
||||
if (decoded.userInfo) {
|
||||
return {
|
||||
status: 1,
|
||||
result: decoded.userInfo
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
status: 403,
|
||||
result: {
|
||||
errInfo: '没有授权'
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
return {
|
||||
status: 503,
|
||||
result: {
|
||||
errInfo: '解密错误'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const Post = (ctx, next) => {
|
||||
switch (ctx.params.action) {
|
||||
case 'check':
|
||||
return CheckAuth(ctx).then(result => { ctx.body = result; next() })
|
||||
default:
|
||||
return CheckAuth(ctx).then(result => { ctx.body = result; next() })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import upload from './upload'
|
||||
import * as api from './api'
|
||||
import * as auth from './auth'
|
||||
|
||||
export default {
|
||||
upload,
|
||||
api,
|
||||
auth
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { System as SystemConfig } from '../config'
|
||||
|
||||
export default (ctx) => {
|
||||
// 设置允许跨域的域名称
|
||||
ctx.set('Access-Control-Allow-Origin', '*')
|
||||
ctx.set('Access-Control-Allow-Headers', 'X-Requested-With')
|
||||
ctx.set('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
|
||||
|
||||
// ----- 情况1:跨域时,先发送一个options请求,此处要返回200 -----
|
||||
if (ctx.method === 'OPTIONS') {
|
||||
console.log('options 请求时,返回 200')
|
||||
|
||||
// 返回结果
|
||||
ctx.status = 200
|
||||
ctx.body = 'options OK'
|
||||
return
|
||||
}
|
||||
|
||||
// ----- 情况2:发送post请求,上传图片 -----
|
||||
|
||||
// 处理 request
|
||||
console.log('parse ok')
|
||||
|
||||
// 文件将要上传到哪个文件夹下面
|
||||
var uploadfolderpath = path.join(__dirname, '../../assets/uploads')
|
||||
|
||||
var files = ctx.request.body.files
|
||||
|
||||
// formidable 会将上传的文件存储为一个临时文件,现在获取这个文件的目录
|
||||
var tempfilepath = files.file.path
|
||||
// 获取文件类型
|
||||
var type = files.file.type
|
||||
|
||||
// 获取文件名,并根据文件名获取扩展名
|
||||
var filename = files.file.name
|
||||
var extname = filename.lastIndexOf('.') >= 0 ? filename.slice(filename.lastIndexOf('.') - filename.length) : ''
|
||||
// 文件名没有扩展名时候,则从文件类型中取扩展名
|
||||
if (extname === '' && type.indexOf('/') >= 0) {
|
||||
extname = '.' + type.split('/')[1]
|
||||
}
|
||||
// 将文件名重新赋值为一个随机数(避免文件重名)
|
||||
filename = Math.random().toString().slice(2) + extname
|
||||
|
||||
// 构建将要存储的文件的路径
|
||||
var filenewpath = path.join(uploadfolderpath, filename)
|
||||
|
||||
var result = ''
|
||||
|
||||
// 将临时文件保存为正式的文件
|
||||
try {
|
||||
fs.renameSync(tempfilepath, filenewpath)
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
// 发生错误
|
||||
console.log('fs.rename err')
|
||||
result = 'error|save error'
|
||||
}
|
||||
}
|
||||
// 保存成功
|
||||
console.log('fs.rename done')
|
||||
// 拼接url地址
|
||||
result = SystemConfig.API_server_type + SystemConfig.API_server_host + ':' + SystemConfig.API_server_port + '/assets/uploads' + filename
|
||||
|
||||
// 返回结果
|
||||
ctx.body = result
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
module.exports = (sequelize, Sequelize) => {
|
||||
const Tutorial = sequelize.define('tutorial', {
|
||||
title: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
description: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
published: {
|
||||
type: Sequelize.BOOLEAN
|
||||
}
|
||||
})
|
||||
|
||||
return Tutorial
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import compose from 'koa-compose'
|
||||
|
||||
function getDirs (srcpath) {
|
||||
return fs.readdirSync(srcpath).filter(file => {
|
||||
return fs.statSync(path.join(srcpath, file)).isDirectory()
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = (srcpath, filename = 'index.js') => {
|
||||
const plugins = {}
|
||||
const dirs = getDirs(srcpath)
|
||||
const list = []
|
||||
|
||||
for (const name of dirs) {
|
||||
let fn = require(path.join(srcpath, name, filename))
|
||||
|
||||
if (typeof fn !== 'function' && typeof fn.default === 'function') {
|
||||
fn = fn.default
|
||||
} else {
|
||||
throw (new Error('plugin must be a function!'))
|
||||
}
|
||||
|
||||
plugins[name] = fn
|
||||
|
||||
list.push(function (ctx, next) {
|
||||
return fn(ctx, next) || next()
|
||||
})
|
||||
}
|
||||
|
||||
return compose(list)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { pgDB as DBConfig } from '../config'
|
||||
const Sequelize = require('sequelize')
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
DBConfig.database,
|
||||
DBConfig.username,
|
||||
DBConfig.password,
|
||||
{
|
||||
host: DBConfig.host,
|
||||
port: DBConfig.port,
|
||||
dialect: 'postgres',
|
||||
pool: {
|
||||
max: 50,
|
||||
min: 0,
|
||||
idle: 10000
|
||||
}
|
||||
})
|
||||
|
||||
const db = {}
|
||||
|
||||
db.Sequelize = Sequelize
|
||||
db.sequelize = sequelize
|
||||
|
||||
db.tutorials = require('../db/schema/tutirial.model')(sequelize, Sequelize)
|
||||
|
||||
module.exports = db
|
||||
@@ -0,0 +1,20 @@
|
||||
module.exports = function () {
|
||||
return function (ctx, next) {
|
||||
return next().catch((err) => {
|
||||
switch (err.status) {
|
||||
case 401:
|
||||
ctx.status = 200
|
||||
ctx.body = {
|
||||
status: 401,
|
||||
result: {
|
||||
err: 'Authentication Error',
|
||||
errInfo: 'Protected resource, use Authorization header to get access.'
|
||||
}
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
const requireDirectory = require('require-directory')
|
||||
module.exports = requireDirectory(module)
|
||||
@@ -0,0 +1,43 @@
|
||||
import nodemailer from 'nodemailer'
|
||||
import { SendEmail } from '../../config'
|
||||
|
||||
export default () => {
|
||||
// console.log('ok')
|
||||
}
|
||||
|
||||
// 发送Email(目前使用的是阿里云SMTP发送邮件)
|
||||
// receivers 目标邮箱,可以用英文逗号分隔多个。(我没试过)
|
||||
// subject 邮件标题
|
||||
// text 文本版本的邮件内容
|
||||
// html HTML版本的邮件内容
|
||||
// 返回
|
||||
// result 200是成功,500是失败
|
||||
// info 是返回的消息,可能是结果的文本,也可能是对象。(这个错误不要暴露给用户)
|
||||
export const sendemail = (receivers, subject, text, html) => {
|
||||
return new Promise(function (resolve) {
|
||||
const transporter = nodemailer.createTransport('smtp://' + SendEmail.username + ':' + SendEmail.password + '@' + SendEmail.service)
|
||||
|
||||
// setup e-mail data with unicode symbols
|
||||
const mailOptions = {
|
||||
from: SendEmail.sender_address, // sender address
|
||||
to: receivers,
|
||||
subject: subject,
|
||||
text: text || 'Hello world 🐴', // plaintext body
|
||||
html: html || '<b>Hello world 🐴</b>' // html body
|
||||
}
|
||||
|
||||
transporter.sendMail(mailOptions, function (error, info) {
|
||||
if (error) {
|
||||
resolve({
|
||||
result: 500,
|
||||
info: error
|
||||
})
|
||||
} else {
|
||||
resolve({
|
||||
result: 200,
|
||||
info: info.response
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = function () {
|
||||
return function (ctx, next) {
|
||||
switch (ctx.status) {
|
||||
case 404:
|
||||
ctx.body = '没有找到内容 - 404'
|
||||
break
|
||||
}
|
||||
return next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import KoaRouter from 'koa-router'
|
||||
import controllers from '../controllers'
|
||||
|
||||
const router = new KoaRouter()
|
||||
|
||||
export default router
|
||||
.get('/public/get', function (ctx, next) {
|
||||
ctx.body = '禁止访问!'
|
||||
}) // 以/public开头则不经过权限认证
|
||||
.all('/upload', controllers.upload)
|
||||
.get('/public/api/:name', controllers.api.Get)
|
||||
.post('/api/:name', controllers.api.Post)
|
||||
.put('/api/:name', controllers.api.Put)
|
||||
.del('/api/:name', controllers.api.Delete)
|
||||
.post('/auth/:action', controllers.auth.Post)
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
SystemConfig
|
||||
} from '../config'
|
||||
|
||||
// 截取字符串,多余的部分用...代替
|
||||
export const setString = (str, len) => {
|
||||
let StrLen = 0
|
||||
let s = ''
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
if (str.charCodeAt(i) > 128) {
|
||||
StrLen += 2
|
||||
} else {
|
||||
StrLen++
|
||||
}
|
||||
s += str.charAt(i)
|
||||
if (StrLen >= len) {
|
||||
return s + '...'
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 格式化设置
|
||||
export const OptionFormat = (GetOptions) => {
|
||||
let options = '{'
|
||||
for (let n = 0; n < GetOptions.length; n++) {
|
||||
options = options + '\'' + GetOptions[n].option_name + '\':\'' + GetOptions[n].option_value + '\''
|
||||
if (n < GetOptions.length - 1) {
|
||||
options = options + ','
|
||||
}
|
||||
}
|
||||
return JSON.parse(options + '}')
|
||||
}
|
||||
|
||||
// 替换SQL字符串中的前缀
|
||||
export const SqlFormat = (str) => {
|
||||
if (SystemConfig.mysql_prefix !== 'api_') {
|
||||
str = str.replace(/api_/g, SystemConfig.mysql_prefix)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// 数组去重
|
||||
export const HovercUnique = (arr) => {
|
||||
const n = {}
|
||||
const r = []
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (!n[arr[i]]) {
|
||||
n[arr[i]] = true
|
||||
r.push(arr[i])
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// 获取json长度
|
||||
export const getJsonLength = (jsonData) => {
|
||||
var arr = []
|
||||
for (var item in jsonData) {
|
||||
arr.push(jsonData[item])
|
||||
}
|
||||
return arr.length
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import app from '../src/app'
|
||||
|
||||
describe('Example', () => {
|
||||
test('should be defined', () => {
|
||||
expect(app).toBeDefined();
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user