service project with demo and testcase
This commit is contained in:
commit
03407638bd
40
api/user.js
Normal file
40
api/user.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import s from "service"
|
||||||
|
import u from 'util'
|
||||||
|
import db from 'db'
|
||||||
|
import verifies from "../lib/verifies"
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
s.register({ method: 'POST', path: '/register', verifies, requires: ['id', 'password', 'name'] }, ({ args }) => {
|
||||||
|
// 生成随机盐,加密密码
|
||||||
|
let salt = u.token(20)
|
||||||
|
let password = u.hex(u.sha256(u.unHex(args.password), salt))
|
||||||
|
|
||||||
|
// 插入用户信息
|
||||||
|
let r = db.insert('User', { id: args.id, password, salt, name: args.name })
|
||||||
|
|
||||||
|
return r.changes > 0 ? { code: 1, message: 'OK' } : { code: 500, message: '注册失败' }
|
||||||
|
})
|
||||||
|
|
||||||
|
s.register({ method: 'POST', path: '/login', verifies, requires: ['id', 'password'] }, ({ args, session }) => {
|
||||||
|
// 读取用户信息
|
||||||
|
let userInfo = db.query1('SELECT `name`,`salt`,`password` FROM User WHERE `id` =?', args.id).result
|
||||||
|
if (!userInfo) return { code: 401, message: '用户或密码验证失败' }
|
||||||
|
|
||||||
|
// 验证密码
|
||||||
|
let password = u.hex(u.sha256(u.unHex(args.password), userInfo.salt))
|
||||||
|
if (password !== userInfo.password) return { code: 401, message: '用户或密码验证失败' }
|
||||||
|
|
||||||
|
// 登录成功,设置session
|
||||||
|
session.set('id', args.id)
|
||||||
|
session.set('name', userInfo.name)
|
||||||
|
session.setAuthLevel(1) // 设置权限等级,其他权限等级为1的接口必须登录成功后才能访问
|
||||||
|
session.save() // 保存session
|
||||||
|
|
||||||
|
return { code: 1, message: 'OK' }
|
||||||
|
})
|
||||||
|
|
||||||
|
s.register({ method: 'GET', path: '/userInfo', authLevel: 1 }, ({ session }) => {
|
||||||
|
// 返回用户信息
|
||||||
|
return { code: 1, message: 'OK', data: session.get('id', 'name') }
|
||||||
|
})
|
||||||
|
}
|
34
apigo.yml
Normal file
34
apigo.yml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name:
|
||||||
|
version: v0.0.1
|
||||||
|
main: main.js
|
||||||
|
target:
|
||||||
|
darwin: amd64 arm64
|
||||||
|
linux: amd64 arm64
|
||||||
|
windows: amd64
|
||||||
|
module:
|
||||||
|
apigo.cc/gojs/console: latest
|
||||||
|
apigo.cc/gojs/util: latest
|
||||||
|
apigo.cc/gojs/log: latest
|
||||||
|
apigo.cc/gojs/file: latest
|
||||||
|
apigo.cc/gojs/http: latest
|
||||||
|
apigo.cc/gojs/db: latest
|
||||||
|
apigo.cc/gojs/service: latest
|
||||||
|
modulealias:
|
||||||
|
extraimport:
|
||||||
|
github.com/go-sql-driver/mysql: latest
|
||||||
|
modernc.org/sqlite: latest
|
||||||
|
cachefile:
|
||||||
|
- api
|
||||||
|
- lib
|
||||||
|
- www
|
||||||
|
sskey:
|
||||||
|
cgoenable: false
|
||||||
|
buildfrom: golang
|
||||||
|
buildenv:
|
||||||
|
linux:
|
||||||
|
darwin:
|
||||||
|
windows:
|
||||||
|
buildldflags:
|
||||||
|
linux:
|
||||||
|
darwin:
|
||||||
|
windows:
|
5
lib/verifies.js
Normal file
5
lib/verifies.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
id: /^\w{3,20}$/,
|
||||||
|
password: /^[a-z0-9]{64}$/,
|
||||||
|
name: /^[a-zA-Z0-9_\-\u4e00-\u9fff]{4,100}$/u,
|
||||||
|
}
|
22
main.js
Normal file
22
main.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import s from 'service'
|
||||||
|
import db from 'db'
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
// 初始化数据库
|
||||||
|
db.make(`
|
||||||
|
User
|
||||||
|
id v20 PK
|
||||||
|
password c64
|
||||||
|
salt c20
|
||||||
|
name v100
|
||||||
|
`)
|
||||||
|
|
||||||
|
s.config({
|
||||||
|
verifyFieldMessage: '参数验证失败:{{FAILED_FIELDS}}'
|
||||||
|
})
|
||||||
|
// 注册服务
|
||||||
|
s.load('api/user.js', { min: 10, max: 100, idle: 20 })
|
||||||
|
|
||||||
|
// 启动服务
|
||||||
|
s.start()
|
||||||
|
}
|
72
main_test.js
Normal file
72
main_test.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import http from 'http'
|
||||||
|
import u from 'util'
|
||||||
|
import file from 'file'
|
||||||
|
|
||||||
|
let bakDB = false
|
||||||
|
|
||||||
|
// 用于测试的http客户端,设置默认URL前缀方便后续调用
|
||||||
|
let hc = http.new({ baseURL: 'http://localhost:8080', timeout: 1000 })
|
||||||
|
|
||||||
|
// 测试开始前,启动服务
|
||||||
|
function onStart() {
|
||||||
|
// 备份数据库文件
|
||||||
|
bakDB = file.exists('user.db')
|
||||||
|
if (bakDB) file.rename('user.db', 'user.db.bak')
|
||||||
|
// 启动服务
|
||||||
|
u.shell(__startExec, 'start')
|
||||||
|
// 等待服务启动
|
||||||
|
u.sleep(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试结束后,停止服务
|
||||||
|
function onStop() {
|
||||||
|
// 停止服务
|
||||||
|
u.shell(__startExec, 'stop')
|
||||||
|
// 恢复数据库文件
|
||||||
|
file.remove('user.db')
|
||||||
|
if (bakDB) file.rename('user.db.bak', 'user.db')
|
||||||
|
}
|
||||||
|
|
||||||
|
function testNotLoggedIn() {
|
||||||
|
// 未登录,应该返回403
|
||||||
|
let r = hc.get('/userInfo')
|
||||||
|
return r.statusCode === 403 || r.status + ' ' + r.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBadUserId() {
|
||||||
|
// 注册时传入错误的用户ID,应该返回400
|
||||||
|
let r = hc.post('/register', { id: 'test.User', password: u.hex(u.sha256('123456')), name: '测试用户' })
|
||||||
|
return r.statusCode === 400 || r.status + ' ' + r.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRegister() {
|
||||||
|
// 注册一个新用户,,code应该返回1
|
||||||
|
let r = hc.post('/register', { id: 'testUser', password: u.hex(u.sha256('123456')), name: '测试用户' })
|
||||||
|
if (r.statusCode !== 200) return r.status + ' ' + r.string()
|
||||||
|
let o = r.object()
|
||||||
|
return o.code === 1 || o
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLoginWithBadPassword() {
|
||||||
|
// 登录时传入错误的密码,code应该返回401
|
||||||
|
let r = hc.post('/login', { id: 'testUser', password: u.hex(u.sha256('1234567')) })
|
||||||
|
if (r.statusCode !== 200) return r.status + ' ' + r.string()
|
||||||
|
let o = r.object()
|
||||||
|
return o.code === 401 || o
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLogin() {
|
||||||
|
// 登录,code应该返回1
|
||||||
|
let r = hc.post('/login', { id: 'testUser', password: u.hex(u.sha256('123456')) })
|
||||||
|
if (r.statusCode !== 200) return r.status + ' ' + r.string()
|
||||||
|
let o = r.object()
|
||||||
|
return o.code === 1 || o
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGetUserInfo() {
|
||||||
|
// 获取用户信息,code应该返回1,id和name应该正确
|
||||||
|
let r = hc.get('/userInfo')
|
||||||
|
if (r.statusCode !== 200) return r.status + ' ' + r.string()
|
||||||
|
let o = r.object()
|
||||||
|
return o.code === 1 && o.data.id === 'testUser' && o.data.name === '测试用户' || o
|
||||||
|
}
|
15
service.yml
Normal file
15
service.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
listen: 8080 # 监听端口
|
||||||
|
ssl: # 设置SSL证书
|
||||||
|
# ____:
|
||||||
|
# certFile:
|
||||||
|
# keyFile:
|
||||||
|
app: '' # 指定应用名称将自动注册为服务,需要配置 registry
|
||||||
|
registry: '' # 服务发现注册中心,请配置一个 redis://:password@127.0.0.1:6379/15
|
||||||
|
accessTokens: # 设置访问令牌,对应的token将获得对应的权限等级
|
||||||
|
# ___: 1
|
||||||
|
calls: # 配置将要调用的服务和访问时使用的令牌
|
||||||
|
# ___: '___'
|
||||||
|
userIdKey: 'id' # 设置存储在Session中的用户ID字段,会将用户ID记录在日志中
|
||||||
|
static: # 设置静态文件目录
|
||||||
|
# '/': 'www/'
|
||||||
|
verifyFieldMessage: 参数验证失败:{{FAILED_FIELDS}}
|
17
www/index.html
Normal file
17
www/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>
|
||||||
|
Hello World
|
||||||
|
</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>
|
||||||
|
Hello World
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user