diff --git a/index.js b/index.js index 0a6f9c1..e08c19b 100644 --- a/index.js +++ b/index.js @@ -2,14 +2,23 @@ import nedb from 'nedb' import express from 'express' import expressWs from 'express-ws' import session from 'express-session' -//import random from 'string-random' +import connect from 'connect-nedb-session' +import random from 'string-random' import formidable from 'formidable' +import md5 from 'md5-node' +import { resolve } from 'path/posix' + + +process.on('SIGINT', function() { + console.log('Got SIGINT. Press Control-D/Control-C to exit.'); + process.exit(0); +}); const app = expressWs(express()).app -//const NedbStore = connect(session) +const NedbStore = connect(session) -// 自动载入所有库 -// 自动构建 db 列表 + +// 数据: 自动构建 db 列表 或自动载入所有库 const databases = new Map() const db = (name) => (databases.get(name) || function(){ let database = new nedb({filename:`./data/db/${name}.db`,autoload:true,timestampData:true}) @@ -17,75 +26,149 @@ const db = (name) => (databases.get(name) || function(){ return database }()) +// 缓存: 自动构建 ws 列表 +const wsstores = new Map() +const wsstore = name => (wsstores.get(name) || function() { + let list = new Map() + wsstores.set(name, list) + return list +}()) + +// 组件: 要求登录 +const online = function(req, res, next) { + req.session.account ? next() : res.status(401).send('未登录') +} + +// 组件: 管理权限 +const admin = function(req, res, next) { + req.session.account.gid === 1 ? next() : res.status(403).send('没有权限') +} + +// 组件: 移除输入非法信息 +const remove = function(req, res, next) { + delete req.body._id + delete req.body.uid + delete req.body.createdAt + delete req.body.updatedAt + delete req.body.views + delete req.body.posts + delete req.body.likes + next() +} + +// 组件: 从缓存载入数据 + +// 组件: 操作权限 +//const authority = function(req, res, next) { +// (req.method === 'GET') ? next() : res.status(200).send +//} + +// 组件: 移除输出敏感信息 + + +app.use(express.json()) +app.use(express.urlencoded({ extended: false })) +app.use(session({ + secret: 'keyboard cat', + name:'sid', + resave: false, + saveUninitialized: false, + cookie: { maxAge: 180 * 24 * 3600000 }, + store: new NedbStore({filename: './data/db/session.db'}), +})) + + +// 只允许管理权限访问 +app.use('/session', online, admin) +app.use('/account', online, admin) +app.use('/message', online, admin) +app.use('/attach', online, admin) +app.use('/like', online, admin) + +// 操作对象必须要登录 +//app.use('/:name', ) + // 对象列表 + + // 对象实体 // 对象附件 // 对象评论 // 对象收藏 // 对象点赞 -app.get('/', function(req, res, next) { - res.send('index') -}) - -app.get('/test', function(req, res, next) { - res.send(` - - test -

2333

-

-
- - -
- - `) -}) - // 无论在哪一级别的对象, 都是泛型对象, 因此不必二级restful // 列表也是对象 //app.use('/:name', function(req, res, next) { // log //}) + +const count_load = async function(name, query) { + await new Promise(resolve => db(name).count(query, function(err, count) { + resolve(count) + })) +} + +const list_load = async function(name, query) { + await new Promise(resolve => db(name).find(query, function(err, docs) { + let list = [] + for (let doc of docs) list.push({_id: doc.id}) + resolve(list) + })) +} + app.get('/:name', function(req, res, next) { - db(req.params.name).find({}, function(err, docs) { + if (req.query.tid) req.query.tid = Number(req.query.tid) // 某些查询参数需要转换类型(暂时) + if (req.query.top) req.query.top = Number(req.query.top) // 某些查询参数需要转换类型(暂时) + if (req.query.uid || req.query.uid !== req.session?.account?.uid) { + req.query.public = true // 如果查询条件限定为自己的, 则不用限制范围到公开的 + } + let { pagesize, page, like, ...query } = req.query + pagesize = Number(pagesize) || 20 + let skip = ((Number(page) || 1) - 1) * pagesize + + // 基于登录状态的查询, 查询点赞过的, 查询评论过的 + if (req.session?.account?.uid) { + if (like) query.$or = list_load('like',{name:req.params.name, uid:req.session.account.uid}) + if (post) query.$or = list_load('post',{name:req.params.name, uid:req.session.account.uid}) + } + 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 (user) item.user = await user_load(item.uid) // 附加用户信息 + } res.json(docs) }) }) -app.put('/:name', function(req, res, next) {}) -app.post('/:name', function(req, res, next) { - db(req.params.name).insert({name:'2333'}, function(err, doc) { - res.json(doc) + +app.post('/:name', online, remove, function(req, res, next) { + req.body.uid = req.session.account.uid // 默认发布者uid + req.body.public = true // 默认发表即公开 + db(req.params.name).insert(req.body, function(err, doc) { + doc ? res.json(doc) : res.status(500).send('内部错误') + }) +}) + +//app.put('/:name', online, function(req, res, next) {}) +//app.patch('/:name', online, function(req, res, next) {}) +//app.head('/:name', function(req, res, next) {}) +//app.options('/:name', function(req, res, next) {}) +app.get('/:name/:id', 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) }) }) -app.patch('/:name', function(req, res, next) {}) -app.head('/:name', function(req, res, next) {}) -app.options('/:name', function(req, res, next) {}) -app.get('/:name/:id', function(req, res, next) {}) app.put('/:name/:id', function(req, res, next) {}) -app.post('/:name/:id', function(req, res, next) {}) +//app.post('/:name/:id', function(req, res, next) {}) app.patch('/:name/:id', function(req, res, next) {}) -app.options('/:name/:id', function(req, res, next) {}) -app.head('/:name/:id', function(req, res, next) {}) +//app.options('/:name/:id', function(req, res, next) {}) +//app.head('/:name/:id', function(req, res, next) {}) app.get('/:name/:id/post', function(req, res, next) { // 获得此对象的所有post记录 diff --git a/package.json b/package.json index 8699ee9..71d97d8 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,13 @@ "author": "satori ", "license": "MIT", "dependencies": { + "connect-nedb-session": "^0.0.3", "express": "^4.17.1", "express-session": "^1.17.2", "express-ws": "^5.0.2", "formidable": "^1.2.2", - "nedb": "^1.8.0" + "md5-node": "^1.0.1", + "nedb": "^1.8.0", + "string-random": "^0.1.3" } } diff --git a/yarn.lock b/yarn.lock index 37856d0..499c6c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,7 +15,7 @@ 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.10, async@~0.2.8: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= @@ -48,6 +48,13 @@ 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" @@ -265,6 +272,11 @@ localforage@^1.3.0: dependencies: lie "3.1.1" +md5-node@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/md5-node/-/md5-node-1.0.1.tgz#0e22d009d46bdc95b1d3c5e8c8feddc1a5c3aa88" + integrity sha1-DiLQCdRr3JWx08XoyP7dwaXDqog= + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -319,6 +331,14 @@ 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" @@ -444,6 +464,11 @@ setprototypeof@1.1.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +string-random@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/string-random/-/string-random-0.1.3.tgz#083f39835c2430fe0be76b9e72f0736e2e0f7b74" + integrity sha512-g+UsIwzKhNi+9/+Q3Q7hP8R4HkQxiIkQlttnxw6GRdk9pnnkGIv53C6H8dvh8wxAVDhkqpnWeauaPXS1b2sBJg== + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"