diff --git a/index.js b/index.js index 8fb0a5c..d503416 100644 --- a/index.js +++ b/index.js @@ -201,47 +201,31 @@ const object_list = async function (req, res) { // 创建对象 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 // 游客和普通用户禁止设置, 统计 + ['_id', 'uid', 'top', 'user', 'createdAt', 'updatedAt', 'views', 'posts', 'likes', 'files'].forEach(i => delete req.body[i]) } - // 如果创建对象是用户作一些特殊处理 + // 如果创建对象是用户作一些特殊处理, 否则创建对象作通用处理 if (req.params.name === 'user') { - if (!req.body.name) { - return res.status(400).send('用户名不能为空') + const { name, password, avatar = '' } = req.body + if (!name) return res.status(400).send('用户名不能为空') + if (!password) return res.status(400).send('密码不能为空') + if (await count_load({ name })) return res.status(400).send('用户名已被占用') + req.body = { + ...req.body, avatar, + gid: (await count_load('user', {})) ? 0 : 1, // 默认是管理员为首个注册用户 + salt: random(32), // 密码加盐 + password: md5(password + random(32)) // 必要设置密码 } - if (!req.body.password) { - return res.status(400).send('密码不能为空') - } - if (await count_load({ name: req.body.name })) { - return res.status(400).send('用户名已被占用') - } - 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) return res.status(401).send('需要登录') - req.body.uid = req.session.account.uid // 为发表对象附上作者ID - req.body.public = true // 默认公开 - req.body.views = 0 // 再生计数 + req.body = { ...req.body, uid: req.session.account.uid, public: true, views: 0 } } - // 如果包含标签 + // 如果包含标签(先查询是否存在, 存在则使用返回的_id进行挂载, 不存在则创建新的) if (req.body.tags && Array.isArray(req.body.tags)) { req.body.tags.forEach(item => { - // 先查询是否存在, 存在则使用返回的_id进行挂载, 不存在则创建新的 db('tag').findOne({ name: item }, function (err, doc) { if (err && !doc) { return // 创建新的 @@ -310,112 +294,8 @@ function object_patch(req, res, next) { } return db(req.params.name).update({ _id: req.params._id }, { $set: req.body }, function (err, count) { if (!count) return res.status(500).send('修改失败') - - // 对象发生了修改, 收集通知用户 - // 执行通知所有关注者 - - // 构建消息内容 - //let data = { name: req.params.name, _id: req.params._id } - - // 如何加入订阅和取消订阅? 如何判断自己是否已经订阅? - // 关注了此对象的用户们(如果存在) - //if (Array.isArray(doc.fm)) { - // doc.fm.forEach(uid => { - // FM.发送消息("PATCH", req.session.account.uid, data) - // // 应当是向每个用户发送消息, 而不是向整个频道发送消息 - // // 或是认为此对象即是一个频道 - // // 此外, 如何判定游客当前客户端的视野? - // // 文章更新但角色未必在线, 而消息通知是要具体到账户, 因而是向消息盒子发生而不是向fm发送 - // // 因此, 此处并不存在频道订阅问题 - // // 而针对正在被观看的, 可以构建频道, 此为有限量, 故而可以临时建立无需落盘的通道(可以是随机生成) - // // 即当对象发生更新时, 调起更新事件, 使用正在观看列表, 向观看状态的ws发送消息(观看状态由ws传递, 断开时取消所有观看状态) - // // 是否为观看状态构建实时更新? - // }) - //} - - // 这个范围过大, 应当是关注此对象的, 而不是关注 PATCH 频道的, 因此 PATCH 是此对象消息的内容 - // 但直接使用对象ID与其它对象重复, 还需要标记对象类型.. - - - // 对象发生了修改, 收集通知终端(也许需要另建一个注视状态绑定) - // let 注视着此对象的终端们 = new Map() - // 如果离开, 则通知 - // 如果已经关注, 则排除对注视终端的重复通知 - - - // 此处插入 hook - // 使用方法: - // kana.item(name || all).patch. - // kana.list(name || all).post - - // 数据双向绑定 - // 某个用户喜欢的主题 - // 主题被哪些用户喜欢 - // 当某个用户不再喜欢某个主题, 要双向解除绑定 - - // like: uid > [pid] - // like: pid > [uid] - // 修改立即存储为日志 - // 逐期将日志合并 - - // attach - // thread > post - // thread < post - // thread 可以存储 [post] - // post 可以存储 [thread] - // 是直接的相互关联, 但数据众多且不在内存中 - // hub的关联在内存中, 但也会向磁盘更新 - // 时常剔除不被使用的, 每次使用使之存活时间增加 - // 每次读取必然缓存一段时间, 而缓存时间由内存压力界定 - // 维持命中率尽量高, 且在接近标准时, 逐渐剔除适合移除的 - // 若是被跑内存呢..? - // 正常情况下数据的访问率如果都非常高, 则适宜加机器 - // 如果异常情况, 则延迟跑内存ip访问速率 - res.send('修改成功') - // 会话完全成功后执行 - //if (typeof (msg) !== "string") return console.log("消息不是字符串") - //let { fm, data } = JSON.parse(msg) - // 此处需要向所有关注此 item 的用户发送消息, 因此还需要向上传递事件(如果这是一个附属对象) - - - // 构建消息内容(由于是作者或管理员修改, 因此不必通知修改者, 消息内容不必特意为修改者书写) - - - // 构建将要接受通知的用户队列, 需要去重, 所以使用 map - // 由于是作者或管理员修改, 因此不必通知修改者, 要将修改者的id特意从最终列表移出 - //let userlist = new Map() - //let collect = () => { - // userlist.set(doc.uid, true) // 先加入本对象作者 - // // 再加入上级关注者. (如果是附属) - // if (doc.attach && doc.aid) { } - // // 再加入下级关注者.(这似乎需要作双向绑定才行) - // if (doc.attachR) { - // // 有哪些类型附属? - // // 每个类型有哪些对象实体? - // // 此类调用涉及了似乎较为庞大的关系网, 当调用具体对象时, 如何不必对下级作全量查询呢? - // // 在顶级加入结构表显然并不合适 - // // 下级格式: - // // then > star { - // // userlist: {} // 只有系统维护的字段, 如果写入, 先转换到 map, 或是直接去重 - // // } - // // 实际就是这个用户是否关注了这个对象, 如果关注了, - // // 如何将上下级关系网中所有用户在不加载大量数据的情况下进行关注状态判定? - // // 分离式: 与账户系统解耦, 方便随时分离和改变数据的存储形式 - // // - // // 如果放了挂载点, 查询上下关联时也要分别读取上下的挂载点, 此时是否对上下也全部加载? - // } - //} - - // 从待通知用户队列移除修改者id - // 执行发送消息 - //let 发送消息 = () => { - // FM.发送消息(fm, uid, data) // 先通知本级关注者(作者) - // if (doc.attach && doc.aid) { //再通知上级关注者() - // return 发送消息() - // } - //} }) }) } @@ -455,21 +335,11 @@ const object_remove = function (req, res) { // 读取对象 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('没有权限读取') - } - if (req.params.name === 'user') { - delete doc.salt - delete doc.password - delete doc.mobile - delete doc.email - } else { - doc.user = await user_load(doc.uid) - } - db(req.params.name).update({ _id: req.params._id }, { $set: { views: doc.views ? doc.views + 1 : 1 } }) + if (!doc) return res.status(404).send('目标资源不存在') + if (!doc.public && doc.uid !== session?.account?.uid) return res.status(403).send('没有权限读取') + if (req.params.name === 'user') ['salt', 'password', 'mobile', 'email'].forEach(field => delete doc[field]) + else doc.user = await user_load(doc.uid) + db(req.params.name).update({ _id: req.params._id }, { $set: { views: (doc.views || 0) + 1 } }) return res.json(doc) }) } @@ -494,22 +364,6 @@ const file_upload = function (req, res) { }) } -const file_temp_list = new Map() - -const upload_file_temp = function (req, res) { - formidable({ - multiples: true, - uploadDir: 'data/file', - keepExtensions: true, - maxFieldsSize: 200 * 1024 * 1024 - }).parse(req, (err, fields, files) => { - let image = files['image'] - if (image) (Array.isArray(image) ? image : [image]).forEach(item => { - file_temp_list.set(item.newFilename, item) // 每帧图像记录到临时表 - }) - }) -} - // 向账户上传文件 (头像, 背景, 其它文件) const uploadfile = function (req, res) { @@ -542,15 +396,7 @@ const uploadfile = function (req, res) { $set: { background: '/data/file/' + list[0].newFilename }, // 替换背景 }) } - //let list = [] - //for (let key in files) { - // console.log(key) - // console.log(files[key].originalFilename) - // //(Array.isArray(files[key]) ? files[key] : [files[key]]).map((data) => { - // // let { filepath, mimetype, newFilename, originalFilename, size } = data - // // list.push({ filepath, mimetype, newFilename, originalFilename, size }) - // //}) - //} + res.json(files) }) }