service project with demo and testcase

This commit is contained in:
Star 2024-10-27 00:42:39 +08:00
commit 03407638bd
8 changed files with 206 additions and 0 deletions

40
api/user.js Normal file
View 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
View 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:

1
db.yml Normal file
View File

@ -0,0 +1 @@
default: sqlite://user.db

5
lib/verifies.js Normal file
View 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
View 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
View 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应该返回1id和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
View 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
View 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>