tinydb
This commit is contained in:
25
ejs/data.ejs
25
ejs/data.ejs
@@ -146,7 +146,7 @@
|
||||
|
||||
<div class="row" >
|
||||
|
||||
<div class="col-xl-6">
|
||||
<div class="col-xl-4">
|
||||
<div class="card shadow mb-4" style="border-radius:10px;min-height:660px">
|
||||
<div style="position: absolute;right:20px;top:20px" class="d-none d-md-block">
|
||||
<span>主题 </span><input id="topicFilter" class="form-control form-control-sm" style="display:inline;width:100px;min-width:100px"></input>
|
||||
@@ -172,7 +172,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="col-xl-4">
|
||||
<div class="card shadow mb-4" style="border-radius:10px">
|
||||
|
||||
<div class="card-body" style="overflow:auto;height:660px">
|
||||
@@ -182,6 +182,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4">
|
||||
<div class="card shadow mb-4" style="border-radius:10px;min-height:660px">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">TinyDB</h6>
|
||||
</div>
|
||||
<div class="card-body" style="overflow:auto;">
|
||||
<table id="special_table" class="table table-striped table-bordered" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>键</th>
|
||||
<th>值</th>
|
||||
<th>更新时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="special_data">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
62
js/data.js
62
js/data.js
@@ -1,3 +1,49 @@
|
||||
function init_special_table(rows) {
|
||||
var tableBody = $('#special_data');
|
||||
tableBody.empty();
|
||||
|
||||
rows.forEach(function(row) {
|
||||
var tr = $('<tr></tr>');
|
||||
tr.append('<td>' + (row.topic || '') + '</td>');
|
||||
tr.append('<td>' + (row.message || '') + '</td>');
|
||||
tr.append('<td>' + (row.time || '') + '</td>');
|
||||
tableBody.append(tr);
|
||||
});
|
||||
|
||||
// 初始化DataTable(如果已初始化则销毁重新初始化)
|
||||
if ($.fn.DataTable.isDataTable('#special_table')) {
|
||||
$('#special_table').DataTable().destroy();
|
||||
}
|
||||
$('#special_table').DataTable({
|
||||
"pageLength": 10,
|
||||
"order": [[2, "desc"]],
|
||||
language: {
|
||||
"sProcessing": "处理中...",
|
||||
"sLengthMenu": "每页 _MENU_ 项",
|
||||
"sZeroRecords": "没有匹配结果",
|
||||
"sInfo": "显示第 _START_ 项至 第 _END_ 项结果,共 _TOTAL_ 项",
|
||||
"sInfoEmpty": "显示第 0 项至 0 项结果,共 0 项",
|
||||
"sInfoFiltered": "(由 _MAX_ 项结果过滤)",
|
||||
"sInfoPostFix": "",
|
||||
"sSearch": "搜索:",
|
||||
"sUrl": "",
|
||||
"sEmptyTable": "表中数据为空",
|
||||
"sLoadingRecords": "载入中...",
|
||||
"sInfoThousands": ",",
|
||||
"oPaginate": {
|
||||
"sFirst": "首页",
|
||||
"sPrevious": "上页",
|
||||
"sNext": "下页",
|
||||
"sLast": "末页"
|
||||
},
|
||||
"oAria": {
|
||||
"sSortAscending": ": 以升序排列此列",
|
||||
"sSortDescending": ": 以降序排列此列"
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function get_data() {
|
||||
$.get('queryHook', function(res) {
|
||||
if (res == 1) {
|
||||
@@ -6,16 +52,26 @@ function get_data() {
|
||||
$("#stop").remove()
|
||||
}
|
||||
$.getJSON('getData', {
|
||||
|
||||
|
||||
}, function(res) {
|
||||
var max = res["max"]
|
||||
$("#prj_num").html(res['count'] + " / " + max)
|
||||
$("#prj_num_bar").attr("aria-valuenow", res['count'])
|
||||
$("#prj_num_bar").attr("aria-valuemax", max)
|
||||
$("#prj_num_bar").css("width", (res['count'] * 100 / max) + "%")
|
||||
globalRows = res["rows"]
|
||||
init_table(res["rows"])
|
||||
|
||||
// 筛选数据:topic不以$开头的存入globalRows,以$开头的存入globalRows2并去掉$
|
||||
globalRows = res["rows"].filter(row => !row.topic || !row.topic.startsWith('$'));
|
||||
globalRows2 = res["rows"]
|
||||
.filter(row => row.topic && row.topic.startsWith('$'))
|
||||
.map(row => ({
|
||||
...row,
|
||||
topic: row.topic.substring(1) // 去掉开头的$
|
||||
}));
|
||||
|
||||
init_table(globalRows) // 初始化表格使用非$开头的数据
|
||||
sync_chart()
|
||||
init_special_table(globalRows2)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
203
mixio.js
203
mixio.js
@@ -1290,6 +1290,209 @@ var mixioServer = async function() {
|
||||
})
|
||||
})
|
||||
|
||||
// tinyWebDB Implementation
|
||||
function validateRequiredParams(req, res) {
|
||||
return new Promise((resolve) => {
|
||||
const { user, secret, action } = req.body;
|
||||
|
||||
db.get("SELECT * FROM `user` WHERE username = ?", [user], function(err, row) {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '数据库查询错误' });
|
||||
}
|
||||
|
||||
if (row) {
|
||||
if (row.password === secret) {
|
||||
if (!action) {
|
||||
res.json({ status: 'error', message: '缺少操作类型参数' });
|
||||
return resolve(true); // 表示已处理响应
|
||||
}
|
||||
|
||||
const validActions = ['update', 'get', 'delete', 'count', 'search'];
|
||||
if (!validActions.includes(action)) {
|
||||
res.json({ status: 'error', message: '无效的操作类型' });
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
return resolve(false); // 验证通过,未处理响应
|
||||
} else {
|
||||
res.json({ status: 'error', message: '用户名或密钥错误' });
|
||||
return resolve(true);
|
||||
}
|
||||
} else {
|
||||
res.json({ status: 'error', message: '用户名或密钥错误' });
|
||||
return resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
app.post('/tinydb', async (req, res) => {
|
||||
const hasError = await validateRequiredParams(req, res);
|
||||
// 如果 validateRequiredParams 已经发送了响应,直接返回
|
||||
if (hasError) {
|
||||
return;
|
||||
}
|
||||
const { user, action, tag, value } = req.body;
|
||||
var hash = 0,
|
||||
i, chr;
|
||||
for (i = 0; i < user.length; i++) {
|
||||
chr = user.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
hash = Math.abs(hash) % 8
|
||||
switch (action) {
|
||||
case 'update':
|
||||
handleUpdate(req, res, hash);
|
||||
break;
|
||||
case 'get':
|
||||
handleGet(req, res, hash);
|
||||
break;
|
||||
case 'delete':
|
||||
handleDelete(req, res, hash);
|
||||
break;
|
||||
case 'count':
|
||||
handleCount(req, res, hash);
|
||||
break;
|
||||
case 'search':
|
||||
handleSearch(req, res, hash);
|
||||
break;
|
||||
default:
|
||||
res.json({ status: 'error', message: '未知操作类型' });
|
||||
}
|
||||
});
|
||||
|
||||
// 更新操作
|
||||
function handleUpdate(req, res, hash) {
|
||||
const { user, tag, value } = req.body;
|
||||
if (!tag || value === undefined) {
|
||||
return res.json({ status: 'error', message: '更新操作需要 tag 和 value 参数' });
|
||||
}
|
||||
reserveDBs[hash].get("SELECT * FROM reserve WHERE topic = ? AND userName = ?", ['$' + tag, user], (err, row) => {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '数据库查询错误' });
|
||||
}
|
||||
if (row) {
|
||||
reserveDBs[hash].run("UPDATE reserve SET message = ?, time = CURRENT_TIMESTAMP WHERE topic = ? AND userName = ?",
|
||||
[value, '$' + tag, user], function(err) {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '更新失败' });
|
||||
}
|
||||
res.json({ status: 'success', message: '更新成功' });
|
||||
});
|
||||
} else {
|
||||
// 插入新记录
|
||||
reserveDBs[hash].run("INSERT INTO reserve (userName, topic, message) VALUES (?, ?, ?)",
|
||||
[user, '$' + tag, value], function(err) {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '插入失败' });
|
||||
}
|
||||
res.json({ status: 'success', message: '更新成功' });
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 读取操作
|
||||
function handleGet(req, res, hash) {
|
||||
const { user, tag } = req.body;
|
||||
|
||||
if (!tag) {
|
||||
return res.json({ status: 'error', message: '读取操作需要 tag 参数' });
|
||||
}
|
||||
|
||||
reserveDBs[hash].get("SELECT * FROM reserve WHERE topic = ? AND userName = ?", ['$' + tag, user], (err, row) => {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '数据库查询错误' });
|
||||
}
|
||||
|
||||
if (row) {
|
||||
res.json({ status: 'success', value: row.message });
|
||||
} else {
|
||||
res.json({ status: 'error', message: '变量不存在' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 删除操作
|
||||
function handleDelete(req, res, hash) {
|
||||
const { user, tag } = req.body;
|
||||
|
||||
if (!tag) {
|
||||
return res.json({ status: 'error', message: '删除操作需要 tag 参数' });
|
||||
}
|
||||
|
||||
reserveDBs[hash].run("DELETE FROM reserve WHERE topic = ? AND userName = ?", ['$' + tag, user], function(err) {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '删除失败' });
|
||||
}
|
||||
|
||||
if (this.changes > 0) {
|
||||
res.json({ status: 'success', message: '删除成功' });
|
||||
} else {
|
||||
res.json({ status: 'error', message: '变量不存在' });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 计数操作
|
||||
function handleCount(req, res, hash) {
|
||||
const { user } = req.body;
|
||||
reserveDBs[hash].get("SELECT COUNT(*) as count FROM reserve WHERE userName = ?", [user], (err, row) => {
|
||||
if (err) {
|
||||
return res.json({ status: 'error', message: '数据库查询错误' });
|
||||
}
|
||||
res.json({ status: 'success', count: row.count });
|
||||
});
|
||||
}
|
||||
|
||||
// 查询操作
|
||||
function handleSearch(req, res, hash) {
|
||||
const { no = 1, count = 1, tag = '', type = 'both', user} = req.body;
|
||||
const startIndex = Math.max(0, parseInt(no) - 1);
|
||||
const limitCount = Math.min(100, parseInt(count)); // 最多返回100条
|
||||
|
||||
let sql = "SELECT * FROM reserve WHERE userName = ?";
|
||||
let params = [user];
|
||||
|
||||
if (tag) {
|
||||
sql += " AND topic LIKE ?";
|
||||
params.push(`%${tag}%`);
|
||||
}
|
||||
|
||||
sql += " ORDER BY time DESC LIMIT ? OFFSET ?";
|
||||
params.push(limitCount, startIndex);
|
||||
|
||||
reserveDBs[hash].all(sql, params, (err, rows) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
return res.json({ status: 'error', message: '数据库查询错误' });
|
||||
}
|
||||
|
||||
// 筛选以$开头的tag,并在返回时去掉$
|
||||
const formattedResult = rows
|
||||
.filter(row => row.topic && row.topic.startsWith('$')) // 筛选$开头的tag
|
||||
.map(row => {
|
||||
const cleanTopic = row.topic.substring(1); // 去掉开头的$
|
||||
|
||||
if (type === 'tag') {
|
||||
return { tag: cleanTopic };
|
||||
} else if (type === 'value') {
|
||||
return { value: row.message };
|
||||
} else {
|
||||
return { tag: cleanTopic, value: row.message };
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
status: 'success',
|
||||
data: formattedResult
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
app.get('/api/v1/getData', function(req, res) {
|
||||
try {
|
||||
if (!(req.query.user && req.query.password && req.query.project && req.query.topic)) {
|
||||
|
||||
88
test.sh
Normal file
88
test.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置 API 地址
|
||||
API_URL="http://localhost:8081/tinydb"
|
||||
|
||||
echo "=== TinyWebDB API 测试 ==="
|
||||
echo "服务器地址: $API_URL"
|
||||
echo
|
||||
|
||||
# 1. 测试更新操作
|
||||
echo "1. 测试更新操作:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=update&tag=test_key&value=Hello World"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 2. 测试读取操作
|
||||
echo "2. 测试读取操作:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=get&tag=test_key"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 3. 测试计数操作
|
||||
echo "3. 测试计数操作:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=count"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 4. 测试查询操作
|
||||
echo "4. 测试查询操作:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=search&count=5"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 5. 测试删除操作
|
||||
echo "5. 测试删除操作:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=delete&tag=test_key"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 6. 测试批量操作
|
||||
echo "6. 测试批量操作 - 插入多条数据:"
|
||||
for i in {1..5}; do
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=update&tag=key_$i&value=Value $i" > /dev/null 2>&1
|
||||
echo "插入 key_$i"
|
||||
done
|
||||
echo
|
||||
|
||||
# 7. 测试条件查询
|
||||
echo "7. 测试条件查询 (tag包含'key'):"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=search&tag=key&count=10"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 8. 测试错误情况 - 认证失败
|
||||
echo "8. 测试错误情况 - 认证失败:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=wrong&secret=wrong&action=get&tag=test_key"
|
||||
echo
|
||||
echo
|
||||
|
||||
# 9. 测试错误情况 - 缺少参数
|
||||
echo "9. 测试错误情况 - 缺少参数:"
|
||||
curl -X POST $API_URL \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "user=1371033826@qq.com&secret=20b7b5eff3bd414ad42d7870feee4f45&action=update&tag=test_key"
|
||||
echo
|
||||
echo
|
||||
|
||||
echo "=== 测试完成 ==="
|
||||
|
||||
# 按任意键退出功能
|
||||
echo "按任意键退出..."
|
||||
read -n 1 -s -r
|
||||
Reference in New Issue
Block a user