重构
This commit is contained in:
parent
2f2568e46f
commit
e01dc765da
49
README.md
49
README.md
@ -19,11 +19,56 @@ node index
|
||||
pm2 start node --name shizukana -- index
|
||||
```
|
||||
|
||||
## doc
|
||||
## 特征
|
||||
|
||||
1. 抽象化以扩大泛用性
|
||||
2. 无须配置依赖项的
|
||||
|
||||
|
||||
### 程序结构
|
||||
1. 抽象细分原则模块化
|
||||
2. 免配置原则
|
||||
3. 分层原则
|
||||
4. 分布式原则
|
||||
5. 解耦扩容原则
|
||||
|
||||
|
||||
|
||||
#### 接口层
|
||||
对外接口允许多服务终端, 服务集群皆为公开阵列
|
||||
允许即刻熔断
|
||||
|
||||
1. 每地区一台或多台伺服器, 伺服器只是节点, 不属于中心
|
||||
2. 具体落盘数据格式与冗余如何与接口节点无关
|
||||
|
||||
则终端连接伺服器通过可用时期节点列表缓存
|
||||
终端连接信任域列表, 使用证书校验节点有效性
|
||||
终端从信任域获取列表, 并将列表缓存, 特定更新周期或事件对伺服器列表更新
|
||||
|
||||
/全功能伺服器
|
||||
任意节点都是伺服器之一, 没有分级且发挥节点所有效能
|
||||
节点给出可用节点列表,
|
||||
|
||||
节点之间如何通信实现可用列表和数据的同步?
|
||||
|
||||
1. 主动从节点阵列获取
|
||||
2. 被动从节点阵列收取
|
||||
|
||||
事件模型中, 当伺服节点产生事件, 向有限域传递事件
|
||||
但由于传递过程时间有滞后, 无法保障事件发生顺序准确性
|
||||
|
||||
|
||||
因而, 有因果勾连的事物须在唯一时空与媒介发生
|
||||
不同节点并不能属于同一时空, 且节点本以在不同时空降低滞后性效用而存在, 以及隔离
|
||||
|
||||
|
||||
小团体伺服节点在不同伺服器之间无感转移(媒介)
|
||||
1. 媒介作为通讯会话, 可轻易建立与销毁
|
||||
2. 伺服器作为沟通节点提供连接,如 websocket
|
||||
3. 在节点与节点之间建立通信, 为媒介
|
||||
4. 多个节点可能同时通信
|
||||
5. 但效率不如此次会话所有终端连接同一伺服节点
|
||||
6. 且算力是定量, 无论节点多少
|
||||
|
||||
### 使用示例
|
||||
|
||||
|
||||
|
420
index.js
420
index.js
@ -1,86 +1,59 @@
|
||||
import nedb from 'nedb'
|
||||
import express from 'express'
|
||||
import expressWs from 'express-ws'
|
||||
import session from 'express-session'
|
||||
import random from 'string-random'
|
||||
import nedb from 'nedb'
|
||||
import express from 'express'
|
||||
import expressWs from 'express-ws'
|
||||
import session from 'express-session'
|
||||
import sessionDb from 'express-session-nedb'
|
||||
import random from 'string-random'
|
||||
import formidable from 'formidable'
|
||||
import md5 from 'md5-node'
|
||||
import md5 from 'md5-node'
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
console.log('Got SIGINT. Press Control-D/Control-C to exit.')
|
||||
process.exit(0)
|
||||
})
|
||||
//process.on('SIGINT', function () {
|
||||
// console.log('Got SIGINT. Press Control-D/Control-C to exit.')
|
||||
// process.exit(0)
|
||||
//})
|
||||
|
||||
const app = expressWs(express()).app
|
||||
const databases = new Map() // 所有数据库
|
||||
const wsstores = new Map() // 所有 websocket 连接
|
||||
//const wsstores = new Map() // 所有 websocket 连接
|
||||
|
||||
const db = (name) => (databases.get(name) || function(){
|
||||
let database = new nedb({filename:`./data/db/${name}.db`,autoload:true,timestampData:true})
|
||||
const db = (name) => (databases.get(name) || function () {
|
||||
let database = new nedb({ filename: `./data/db/${name}.db`, autoload: true, timestampData: true })
|
||||
databases.set(name, database)
|
||||
return database
|
||||
}())
|
||||
|
||||
// 通道: 自动构建 ws 列表
|
||||
const wsstore = name => (wsstores.get(name) || function() {
|
||||
let list = new Map()
|
||||
wsstores.set(name, list)
|
||||
return list
|
||||
}())
|
||||
// const wsstore = name => (wsstores.get(name) || function () {
|
||||
// let list = new Map()
|
||||
// wsstores.set(name, list)
|
||||
// return list
|
||||
// }())
|
||||
|
||||
// 组件: 要求登录 (普通成员路由参数附上uid以分离权限)
|
||||
const online = function(req, res, next) {
|
||||
const session_store = sessionDb(session, db('session'))
|
||||
|
||||
// 登录验证
|
||||
const online = function (req, res, next) {
|
||||
if (!req.session.account) return res.status(401).send('未登录')
|
||||
if (req.session.account.gid != 1) req.params.uid = req.session.account.uid
|
||||
next()
|
||||
}
|
||||
|
||||
const SessionStore = function (session) {
|
||||
function NedbStore(options, cb) {
|
||||
var callback = cb || function () {};
|
||||
this.db = db('session');
|
||||
this.db.loadDatabase(callback);
|
||||
}
|
||||
NedbStore.prototype.__proto__ = session.Store.prototype;
|
||||
NedbStore.prototype.get = function (sid, callback) {
|
||||
this.db.findOne({ sid: sid }, function (err, sess) {
|
||||
if (err) { return callback(err); }
|
||||
if (!sess) { return callback(null, null); }
|
||||
|
||||
return callback(null, sess.data);
|
||||
});
|
||||
};
|
||||
NedbStore.prototype.set = function (sid, data, callback) {
|
||||
this.db.update({ sid: sid }, { sid: sid, data: data }, { multi: false, upsert: true }, function (err) {
|
||||
return callback(err);
|
||||
});
|
||||
};
|
||||
NedbStore.prototype.destroy = function (sid, callback) {
|
||||
this.db.remove({ sid: sid }, { multi: false }, function (err) {
|
||||
return callback(err);
|
||||
});
|
||||
};
|
||||
return NedbStore;
|
||||
};
|
||||
|
||||
const session_store = new (SessionStore(session))
|
||||
|
||||
// 列表计量
|
||||
const count_load = async (name, query) => await new Promise(resolve => db(name).count(query, (err, count) => resolve(count)))
|
||||
|
||||
// 条件查询
|
||||
const list_load = async (name, query) => await new Promise(resolve => db(name).find(query, function(err, docs) {
|
||||
return resolve(docs.Map((item, index) => Object.assign({}, {_id: item.id})))
|
||||
const list_load = async (name, query) => await new Promise(resolve => db(name).find(query, function (err, docs) {
|
||||
return resolve(docs.Map((item, index) => Object.assign({}, { _id: item.id })))
|
||||
}))
|
||||
|
||||
const user_load = async (_id) => await new Promise(resolve => db('user').findOne({_id}, function(err, doc) {
|
||||
const user_load = async (_id) => await new Promise(resolve => db('user').findOne({ _id }, function (err, doc) {
|
||||
if (!doc) return resolve(doc)
|
||||
let { salt, password, mobile, email, ...user } = doc
|
||||
return resolve(user)
|
||||
}))
|
||||
|
||||
// 特定类型查询时参数特性: message
|
||||
const message = async function(req, res, next) {
|
||||
const message = async function (req, res, next) {
|
||||
if (req.query.unread) req.query.unread = (req.query.unread === 'true')
|
||||
if (req.query.archive) req.query.archive = (req.query.archive === 'true')
|
||||
if (req.query.to) {
|
||||
@ -91,153 +64,35 @@ const message = async function(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
// 标准列表
|
||||
const ListView = async function(req, res, next) {
|
||||
let { pagesize, page, count, like, post, tid, top, uid, user, ...query } = req.query
|
||||
if (tid) query.tid = Number(tid) // 某些查询参数需要转换类型
|
||||
if (top) query.top = Number(top) // 某些查询参数需要转换类型
|
||||
if (uid && uid !== req.session?.account?.uid) query.public = true // 如果查询条件限定为自己的, 则不用限制范围到公开的
|
||||
|
||||
page = Number(page) || 1 // 默认页码1
|
||||
pagesize = Number(pagesize) || 20 // 默认分页20
|
||||
let skip = (page - 1) * pagesize // 截取点
|
||||
|
||||
// 基于登录状态的查询, 查询点赞过的, 查询评论过的
|
||||
if (req.session?.account?.uid) {
|
||||
if (like) query.$or = await list_load('like',{name:req.params.name, uid:req.session.account.uid})
|
||||
if (post) query.$or = await list_load('post',{name:req.params.name, uid:req.session.account.uid})
|
||||
}
|
||||
if (count) {
|
||||
await new Promise(resolve => db(req.params.name).count(query, function(err, count) {
|
||||
res.header('count', count)
|
||||
res.header('page', page)
|
||||
res.header('pages', Math.ceil(count/pagesize))
|
||||
res.header('pagesize', pagesize)
|
||||
resolve()
|
||||
}))
|
||||
}
|
||||
db(req.params.name).find(query).skip(skip).limit(pagesize).sort({createdAt: -1}).exec(async function(err, docs) {
|
||||
for (let item of docs) {
|
||||
item.posts = await count_load('post', {name:req.params.name, id:item._id}) // 附加评论数量
|
||||
item.likes = await count_load('like', {name:req.params.name, id:item._id}) // 附加点赞数量
|
||||
}
|
||||
if (!req.params.name) {
|
||||
docs.forEach(async item => {
|
||||
let { salt, password, mobile, email, ...user } = item
|
||||
item = user
|
||||
})
|
||||
}
|
||||
if (user && req.params.name !== 'user') for (let item of docs) {
|
||||
item.user = await user_load(item.uid)
|
||||
}
|
||||
res.json(docs)
|
||||
})
|
||||
}
|
||||
|
||||
const OneView = async function(req, res, next) {
|
||||
db(req.params.name).findOne({_id: req.params._id}, async function(err, doc) {
|
||||
if (err || !doc) return res.status(404).send('目标资源不存在')
|
||||
if (!doc.public && doc.uid !== req.session?.account?.uid) {
|
||||
return res.status(403).send('没有权限读取')
|
||||
}
|
||||
// 附加用户信息
|
||||
if (req.query.user) doc.user = await user_load(doc.uid)
|
||||
res.send(doc)
|
||||
})
|
||||
}
|
||||
|
||||
const object_create = async function(req, res, next) {
|
||||
if (req.session?.account?.gid != 1) {
|
||||
delete req.body._id // 普通用户禁止设置, 权限
|
||||
delete req.body.uid // 普通用户禁止设置, 权限
|
||||
delete req.body.top // 普通用户禁止设置, 权限
|
||||
delete req.body.user // 普通用户禁止设置, 计算
|
||||
delete req.body.createdAt // 普通用户禁止设置, 自动
|
||||
delete req.body.updatedAt // 普通用户禁止设置, 自动
|
||||
delete req.body.views // 普通用户禁止设置, 统计
|
||||
delete req.body.posts // 普通用户禁止设置, 统计
|
||||
delete req.body.likes // 普通用户禁止设置, 统计
|
||||
delete req.body.files // 普通用户禁止设置, 统计
|
||||
}
|
||||
if (!req.params.name) {
|
||||
req.body.name = req.body.name || random(12) // 默认用户名
|
||||
req.body.avatar = req.body.avatar || '' // 默认用户头像
|
||||
req.body.gid = (await count_load('user', {})) ? 0 : 1 // 默认是管理员为首个注册用户
|
||||
req.body.salt = random(32) // 密码加盐
|
||||
req.body.password = md5(req.body.password + req.body.salt) // 必要设置密码
|
||||
req.body.public = true // 默认公开
|
||||
} else if (req.session?.account?.uid) {
|
||||
req.body.uid = req.session.account.uid // 为发表对象附上作者ID
|
||||
req.body.public = true // 默认公开
|
||||
}
|
||||
db(req.params.name ?? 'user').insert(req.body, function(err, doc) {
|
||||
doc ? res.json(doc) : res.status(500).send('创建失败')
|
||||
})
|
||||
}
|
||||
|
||||
const session_list = (req, res) => session_store.db.find({ "data.account.uid": req.session.account.uid }, function(err, docs) {
|
||||
const session_list = (req, res) => session_store.db.find({ "data.account.uid": req.session.account.uid }, function (err, docs) {
|
||||
err ? res.status(500).send('错误') : res.json(docs)
|
||||
})
|
||||
|
||||
const session_create = (req, res) => db('user').findOne({name: req.body.name}, function(err, doc) {
|
||||
const session_create = (req, res) => db('user').findOne({ name: req.body.name }, function (err, doc) {
|
||||
if (!doc) return res.status(400).send('账户不存在')
|
||||
if (md5(req.body.password + doc.salt) !== doc.password) return res.status(400).send('密码错误')
|
||||
req.session.regenerate(function(err) {
|
||||
req.session.regenerate(function (err) {
|
||||
req.session.account = { uid: doc._id, gid: doc.gid ?? 0 }
|
||||
let { salt, password, ...user} = doc
|
||||
let { salt, password, ...user } = doc
|
||||
res.json(user)
|
||||
})
|
||||
})
|
||||
|
||||
const session_delete_self = (req, res) => req.session.destroy(function(err) {
|
||||
err ? res.status(500).send('错误') : res.send('退出登录')
|
||||
})
|
||||
const sessionDeleteSelf = function (req, res) {
|
||||
return req.session.destroy(function (err) {
|
||||
return res.status(err ? 500 : 200).send(err ? '错误' : '退出登录')
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: 必须是自己的 UID
|
||||
const session_delete = (req, res) => req.sessionStore.destroy(req.params.sid, function(err) {
|
||||
const session_delete = (req, res) => req.sessionStore.destroy(req.params.sid, function (err) {
|
||||
err ? res.status(500).send('错误') : res.send('退出登录')
|
||||
})
|
||||
|
||||
const home = (req, res) => res.send(`<DOCTYPE html><p> Hello World</p>`)
|
||||
const files_delete = (req, res) => res.status(400).send('拒绝操作')
|
||||
const files_upload = (req, res) => {
|
||||
formidable({ multiples: true, uploadDir: 'data/file', keepExtensions: true, maxFieldsSize: 200 * 1024 * 1024 }).parse(req, function(err, fields, files) {
|
||||
let list = []
|
||||
for (let key in files) {
|
||||
let arr = Array.isArray(files[key]) ? files[key] : [files[key]]
|
||||
arr.forEach(({size, path, name, type}) => list.push({size, path, name, type}))
|
||||
}
|
||||
res.json(list)
|
||||
//db(req.params.name).update({_id:req.params._id}, {$addToSet: {file:list}}, {}, function (err, count, docs) {
|
||||
// if (!count) return res.status(404).send('目标挂载对象不存在')
|
||||
// res.send(docs)
|
||||
//})
|
||||
})
|
||||
}
|
||||
|
||||
const object_remove = async function(req, res, next) {
|
||||
// TODO: 账户操作 会话操作 收藏操作
|
||||
if (!req.params.name) {
|
||||
if (req.session.account.gid !== 1 && req.session.account.uid !== req.params.id) {
|
||||
return res.status(400).send('没有权限删除此账户')
|
||||
}
|
||||
if (await count_load('account', {_id: req.params.id, gid: 1}) === 1) {
|
||||
return res.status(400).send('不可以删除唯一的管理员账户')
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 先移除依赖数据 like post...
|
||||
let {name, ...query} = req.params
|
||||
db(name).remove(query, function(err, count) {
|
||||
count ? res.send('删除成功') : res.status(403).send('删除失败')
|
||||
// TODO: 当对象被删除时通过此连接通知所有在线终端
|
||||
})
|
||||
}
|
||||
|
||||
// app.use('/like', online, admin)
|
||||
|
||||
const profile = function(req, res) {
|
||||
return db('user').findOne({_id: req.session.account.uid}, function(err, doc) {
|
||||
const profile = function (req, res) {
|
||||
return db('user').findOne({ _id: req.session.account.uid }, function (err, doc) {
|
||||
if (err) return res.status(401).send('尚未登录')
|
||||
delete doc.salt
|
||||
delete doc.password
|
||||
@ -245,23 +100,200 @@ const profile = function(req, res) {
|
||||
})
|
||||
}
|
||||
|
||||
// 当你需要指定权限 express-power
|
||||
// 权力与会话同级, 但不是每个会话都复制一份
|
||||
// 有限会话授权, 如果授权者权力过期, 需要向下传播, 即使用的仍旧是授权者所拥有的权柄
|
||||
// 列表对象
|
||||
const object_list = async function (req, res) {
|
||||
let { pagesize, page, count, like, post, tid, top, uid, user, ...query } = req.query
|
||||
|
||||
if (tid) query.tid = Number(tid) // 某些查询参数需要转换类型
|
||||
if (top) query.top = Number(top) // 某些查询参数需要转换类型
|
||||
if (uid && uid !== req.session?.account?.uid) query.public = true // 如果查询条件限定为自己的, 则不用限制范围到公开的
|
||||
|
||||
page = Number(page) || 1 // 默认页码1
|
||||
pagesize = Number(pagesize) || 20 // 默认分页20
|
||||
let skip = (page - 1) * pagesize // 截取点
|
||||
|
||||
// 登录状态时, 查询自己点赞过的和评论过的
|
||||
if (req.session?.account?.uid) {
|
||||
if (like) query.$or = await list_load('like', { attach: req.params.name, uid: req.session.account.uid })
|
||||
if (post) query.$or = await list_load('post', { attach: req.params.name, uid: req.session.account.uid })
|
||||
}
|
||||
|
||||
// 要求附带统计信息
|
||||
if (count) await new Promise(resolve => db(req.params.name).count(query, function (err, count) {
|
||||
res.header('count', count)
|
||||
res.header('page', page)
|
||||
res.header('pages', Math.ceil(count / pagesize))
|
||||
res.header('pagesize', pagesize)
|
||||
resolve()
|
||||
}))
|
||||
|
||||
return db(req.params.name).find(query).skip(skip).limit(pagesize).sort({ createdAt: -1 }).exec(async function (err, docs) {
|
||||
return res.json(await Promise.all(docs.map(async item => {
|
||||
item.posts = await count_load('post', { attach: req.params.name, aid: item._id }) // 附加评论数量
|
||||
item.likes = await count_load('like', { attach: req.params.name, aid: item._id }) // 附加点赞数量
|
||||
item.user = await user_load(item.uid) // 附加用户信息(user对象没有作者)
|
||||
if (req.params.name === 'user') {
|
||||
delete item.salt
|
||||
delete item.password
|
||||
delete item.mobile
|
||||
delete item.email
|
||||
}
|
||||
return item
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
// 创建对象
|
||||
const object_create = async function (req, res) {
|
||||
|
||||
if (req.session?.account?.gid != 1) {
|
||||
delete req.body._id // 游客和普通用户禁止设置, 权限
|
||||
delete req.body.uid // 游客和普通用户禁止设置, 权限
|
||||
delete req.body.top // 游客和普通用户禁止设置, 权限
|
||||
delete req.body.user // 游客和普通用户禁止设置, 计算
|
||||
delete req.body.createdAt // 游客和普通用户禁止设置, 自动
|
||||
delete req.body.updatedAt // 游客和普通用户禁止设置, 自动
|
||||
delete req.body.views // 游客和普通用户禁止设置, 统计
|
||||
delete req.body.posts // 游客和普通用户禁止设置, 统计
|
||||
delete req.body.likes // 游客和普通用户禁止设置, 统计
|
||||
delete req.body.files // 游客和普通用户禁止设置, 统计
|
||||
}
|
||||
|
||||
// 如果创建对象是用户作一些特殊处理
|
||||
if (req.params.name === 'user') {
|
||||
req.body.name = req.body.name || random(12) // 默认用户名(检查用户名是否可用)
|
||||
req.body.avatar = req.body.avatar || '' // 默认用户头像
|
||||
req.body.gid = (await count_load('user', {})) ? 0 : 1 // 默认是管理员为首个注册用户
|
||||
req.body.salt = random(32) // 密码加盐
|
||||
req.body.password = md5(req.body.password + req.body.salt) // 必要设置密码
|
||||
req.body.public = true // 默认公开
|
||||
} else {
|
||||
req.body.uid = req.session.account.uid // 为发表对象附上作者ID
|
||||
req.body.public = true // 默认公开
|
||||
req.body.views = 0 // 再生计数
|
||||
}
|
||||
|
||||
// 如果是挂载对象到指定目标
|
||||
if (req.body.attach && req.body.aid) {
|
||||
let count = await count_load(req.body.attach, { _id: req.body.aid })
|
||||
if (!count) return res.status(404).send('目标挂载对象不存在')
|
||||
}
|
||||
|
||||
// 写入对象
|
||||
return db(req.params.name).insert(req.body, async function (err, doc) {
|
||||
if (!doc) return res.status(500).send('创建失败')
|
||||
if (req.params.name !== 'user') doc.user = await user_load(doc.uid)
|
||||
return res.json(doc)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除对象
|
||||
const object_remove = function (req, res) {
|
||||
return db(req.params.name).findOne({ _id: req.params._id }, async function (err, doc) {
|
||||
if (doc) return res.status(404).send('目标对象不存在')
|
||||
|
||||
// 如果是删除用户作一些特殊处理
|
||||
if (req.params.name === 'user') {
|
||||
if (req.session.account.gid !== 1 && req.session.account.uid !== doc._id) {
|
||||
return res.status(400).send('没有权限删除此账户')
|
||||
}
|
||||
if (await count_load('account', { _id: req.params.id, gid: 1 }) === 1) {
|
||||
return res.status(400).send('不可以删除唯一的管理员账户')
|
||||
}
|
||||
} else {
|
||||
if (req.session.account.gid !== 1 && req.session.account.uid !== doc.uid) {
|
||||
return res.status(403).send('没有权限删除此对象')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理掉一些附属对象
|
||||
|
||||
return db(req.params.name).remove({ _id: req.params._id }, function (err, count) {
|
||||
return count ? res.send('删除成功') : res.status(403).send('删除失败')
|
||||
// TODO: 当对象被删除时通过此连接通知所有在线终端
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 读取对象
|
||||
const object_load = function (req, res) {
|
||||
return db(req.params.name).findOne({ _id: req.params._id }, async function (err, doc) {
|
||||
if (!doc) return res.status(404).send('目标资源不存在')
|
||||
if (!doc.public && doc.uid !== session?.account?.uid) return res.status(403).send('没有权限读取')
|
||||
db(req.params.name).update({ _id }, { $set: { views: doc.views ? doc.views + 1 : 1 } })
|
||||
return res.status(200).json({ user: await user_load(doc.uid), ...doc })
|
||||
})
|
||||
}
|
||||
|
||||
// 修改对象
|
||||
const object_patch = function (req, res) {
|
||||
return db(req.params.name).findOne({ _id: req.params._id }, function (err, doc) {
|
||||
if (!doc) return res.status(404).send('目标对象不存在')
|
||||
|
||||
// 如果是 user 做一些特殊处理
|
||||
if (res.params.name === 'user') {
|
||||
if (res.session.account.uid !== doc._id && res.session.account.gid !== 1) {
|
||||
return res.status(403).send('没有权限修改账户')
|
||||
}
|
||||
if (res.body.gid && res.session.account.gid !== 1) {
|
||||
return res.status(403).send('没有权限修改权限')
|
||||
}
|
||||
if (res.body.password) {
|
||||
req.body.salt = random(32) // 密码加盐
|
||||
res.body.password = md5(req.body.password + req.body.salt) // 设置密码
|
||||
}
|
||||
if (res.body.name) {
|
||||
// 检查用户名是否可用
|
||||
}
|
||||
} else {
|
||||
if (res.session.account.uid !== doc.uid && res.session.account.gid !== 1) {
|
||||
return res.status(403).send('没有权限修改对象')
|
||||
}
|
||||
if (res.body.uid && res.session.account.gid !== 1) {
|
||||
return res.status(403).send('没有权限修改归属')
|
||||
}
|
||||
}
|
||||
return db(req.params.name).update({ _id: req.params._id }, data, function (err, count) {
|
||||
if (!count) return res.status(500).send('修改失败')
|
||||
return res.send('修改成功')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 附件上传
|
||||
const file_upload = function (req, res) {
|
||||
return db(req.params.name).findOne({ _id: req.params._id }, function (err, doc) {
|
||||
if (!doc) return res.status(404).send('目标对象不存在')
|
||||
if (req.session.account.uid !== doc.uid && req.session.account.gid !== 1) {
|
||||
return res.status(403).send('没有权限上传')
|
||||
}
|
||||
return formidable({ multiples: true, uploadDir: 'data/file', keepExtensions: true, maxFieldsSize: 200 * 1024 * 1024 }).parse(req, function (err, fields, files) {
|
||||
let list = []
|
||||
for (let key in files) {
|
||||
(Array.isArray(files[key]) ? files[key] : [files[key]]).map(({ filepath, mimetype, mtime, newFilename, originalFilename, size }) => list.push({
|
||||
filepath, mimetype, mtime, newFilename, originalFilename, size
|
||||
}))
|
||||
}
|
||||
return db(req.params.name).update({ _id: req.params._id }, { $addToSet: { file: { $each: list } } }, function (err, count) {
|
||||
if (!count) return res.status(500).send('附件挂载对象失败')
|
||||
console.log(list)
|
||||
return res.json(list)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
app.use(express.json())
|
||||
app.use(express.urlencoded({ extended: false }))
|
||||
app.use(session({secret: 'shizukana', name:'sid', resave: false, saveUninitialized: false, cookie: { maxAge: 180 * 24 * 3600000 }, store: session_store}))
|
||||
app.use(session({ secret: 'shizukana', name: 'sid', resave: false, saveUninitialized: false, cookie: { maxAge: 180 * 24 * 3600000 }, store: session_store }))
|
||||
app.use('/data/file/', express.static('data/file'))
|
||||
|
||||
app.route('/').get(home)
|
||||
app.route('/user').post(object_create)
|
||||
app.route('/account').get(online, profile)
|
||||
app.route('/session').get(online, session_list).post(session_create).delete(online, session_delete_self)
|
||||
app.route('/session/:sid').delete(online, session_delete) // 会话
|
||||
app.route('/:name').get(ListView).post(online, object_create) // 列表
|
||||
app.route('/:name/:_id').get(OneView).put().patch().delete(online, object_remove) // 对象
|
||||
app.route('/:name/:_id/files').post(online, files_upload) // 附件
|
||||
app.route('/:name/:_id/files/:id').delete(online, files_delete) // 附件
|
||||
app.route('/session').get(online, session_list).post(session_create).delete(online, sessionDeleteSelf)
|
||||
app.route('/session/:sid').delete(online, session_delete)
|
||||
app.route('/:name').get(object_list).post(online, object_create)
|
||||
app.route('/:name/:_id').get(object_load).post(online, file_upload).put().patch(online, object_patch).delete(online, object_remove)
|
||||
|
||||
app.listen(2333)
|
||||
|
@ -7,11 +7,11 @@
|
||||
"author": "satori <huan0016@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"connect-nedb-session": "^0.0.3",
|
||||
"express": "^4.17.1",
|
||||
"express-session": "^1.17.2",
|
||||
"express-session-nedb": "^1.0.1",
|
||||
"express-ws": "^5.0.2",
|
||||
"formidable": "^1.2.2",
|
||||
"formidable": "^2.0.1",
|
||||
"md5-node": "^1.0.1",
|
||||
"nedb": "^1.8.0",
|
||||
"string-random": "^0.1.3"
|
||||
|
98
yarn.lock
98
yarn.lock
@ -15,7 +15,12 @@ array-flatten@1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||
|
||||
async@0.2.10, async@~0.2.8:
|
||||
asap@^2.0.0:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
async@0.2.10:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
|
||||
integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
|
||||
@ -48,13 +53,6 @@ bytes@3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
|
||||
|
||||
connect-nedb-session@^0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/connect-nedb-session/-/connect-nedb-session-0.0.3.tgz#e10f642d3d604f609bb23a450021dd1f0579c5ac"
|
||||
integrity sha1-4Q9kLT1gT2CbsjpFACHdHwV5xaw=
|
||||
dependencies:
|
||||
nedb "0.0.6"
|
||||
|
||||
content-disposition@0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
|
||||
@ -104,6 +102,14 @@ destroy@~1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||
|
||||
dezalgo@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
|
||||
integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
|
||||
dependencies:
|
||||
asap "^2.0.0"
|
||||
wrappy "1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@ -124,6 +130,11 @@ etag@~1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
express-session-nedb@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/express-session-nedb/-/express-session-nedb-1.0.1.tgz#0fbb148d24db24bcc7b33b1c478f6fddca04c851"
|
||||
integrity sha512-06J0wSG+GRx0cThEtlmwPUIByjH0zHB0KqRe3mutow7IBsaFdOmHIFOeVnASxOIBahhAaglaeCxo+Sat8eo1Ow==
|
||||
|
||||
express-session@^1.17.2:
|
||||
version "1.17.2"
|
||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd"
|
||||
@ -194,10 +205,15 @@ finalhandler@~1.1.2:
|
||||
statuses "~1.5.0"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
formidable@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
|
||||
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
|
||||
formidable@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff"
|
||||
integrity sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==
|
||||
dependencies:
|
||||
dezalgo "1.0.3"
|
||||
hexoid "1.0.0"
|
||||
once "1.4.0"
|
||||
qs "6.9.3"
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
@ -209,6 +225,11 @@ fresh@0.5.2:
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
hexoid@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||
|
||||
http-errors@1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
|
||||
@ -266,9 +287,9 @@ lie@3.1.1:
|
||||
immediate "~3.0.5"
|
||||
|
||||
localforage@^1.3.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1"
|
||||
integrity sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
||||
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
@ -292,17 +313,17 @@ methods@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
||||
mime-db@1.48.0:
|
||||
version "1.48.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d"
|
||||
integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
|
||||
mime-db@1.51.0:
|
||||
version "1.51.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c"
|
||||
integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==
|
||||
|
||||
mime-types@~2.1.24:
|
||||
version "2.1.31"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b"
|
||||
integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
|
||||
version "2.1.34"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24"
|
||||
integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==
|
||||
dependencies:
|
||||
mime-db "1.48.0"
|
||||
mime-db "1.51.0"
|
||||
|
||||
mime@1.6.0:
|
||||
version "1.6.0"
|
||||
@ -331,14 +352,6 @@ ms@2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
nedb@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/nedb/-/nedb-0.0.6.tgz#a2e6c02cb2fcacf91245b02431f44cf1664aa85f"
|
||||
integrity sha1-oubALLL8rPkSRbAkMfRM8WZKqF8=
|
||||
dependencies:
|
||||
async "~0.2.8"
|
||||
underscore "~1.4.4"
|
||||
|
||||
nedb@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/nedb/-/nedb-1.8.0.tgz#0e3502cd82c004d5355a43c9e55577bd7bd91d88"
|
||||
@ -367,6 +380,13 @@ on-headers@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
|
||||
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
|
||||
|
||||
once@1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
@ -390,6 +410,11 @@ qs@6.7.0:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@6.9.3:
|
||||
version "6.9.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
|
||||
integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
|
||||
|
||||
random-bytes@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
@ -509,7 +534,12 @@ vary@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@^7.4.6:
|
||||
version "7.5.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
|
||||
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
|
||||
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
|
||||
|
Loading…
Reference in New Issue
Block a user