diff --git a/main.js b/main.js index beca64c..b2b60ca 100644 --- a/main.js +++ b/main.js @@ -1,9 +1,43 @@ -import { div, span, main, section, aside, h3, p, ul, li } from '@laniakeasupercluster/widgets' +import { div, span, main, section, aside, h3, p, ul, li, header, nav, a } from '@laniakeasupercluster/widgets' + +const navData = [ + { id: '1', name: 'Home', path: '/' }, + { id: '2', name: 'Blog', path: '/blog' }, + { id: '3', name: 'About', path: '/about' }, + { id: '4', name: 'Contact', path: '/contact' } +] + +document.body.appendChild(header.flex({ justifyContent: 'space-between', alignItems: 'center' }).childs([ + nav.flex({ gap: '1rem' }).childs(navData.map(item => { + const link = document.createElement('a') + link.href = `/${item.path}` + link.textContent = item.name + link.style.padding = '.5rem' + link.style.borderRadius = '.25rem' + link.style.color = (item.path === window.location.pathname) ? '#00C16A' : '#333' + return link + })), + div.text('sign'), +])) + +const blogata = await fetch('/api/blog').then(res => res.text()) +console.log(blogata) const blogData = [ { id: '1', title: 'Blog 1', content: 'Content 1', date: new Date() }, { id: '2', title: 'Blog 2', content: 'Content 2', date: new Date() }, { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, + { id: '3', title: 'Blog 3', content: 'Content 3', date: new Date() }, ] const tagData = ['JavaScript', 'CSS', 'HTML', 'Web', 'JavaScript', 'CSS', 'HTML', 'Web', 'JavaScript', 'CSS', 'HTML', 'Web', 'JavaScript', 'CSS', 'HTML', 'Web'] const articleData = ['Article 1', 'Article 2', 'Article 3'] @@ -14,17 +48,53 @@ document.body.appendChild(div.grid({ gridTemplateColumns: '3fr 1fr', gap: '1rem' p.text(blog.content), p.text(blog.date), ]))), - aside.grid({ gridColumn: '2', flexDirection: 'column', gap: '1rem' }).childs([ + aside.flex({ gridColumn: '2', flexDirection: 'column', gap: '1rem' }).childs([ div.childs([ - h3.font('bold').text('# TAG'), + h3.font('bold').text('TAG'), div.flex({ gap: '.5rem', flexWrap: 'wrap' }).childs([ // cursor-pointer overflow-clip hover:text-pink-500 ...tagData.map(tag => span.bg('rgba(200,200,200,.2)').radius('.25rem').px('8px').text(tag)) ]), ]), div.childs([ - h3.font('bold 8px').text('# 归档'), + h3.font('bold').text('归档'), ul.childs(articleData.map(article => li.text(article))), ]), ]), ])) + +const style = document.createElement('style') +style.textContent = ` + body { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + font-family: inherit; + } + body > header { + gap: 1rem; + width: 100%; + line-height: 4rem; + border-bottom: 1px solid #f5f6f5; + } + body header nav { + display: flex; + gap: 1rem; + font-weight: bold; + font-size: 1.25rem; + width: 100%; + max-width: 1280px; + margin: 0 auto; + padding: 0 1.3rem; + box-sizing: border-box; + } + body aside h3 { + font-size: 1rem; + font-weight: 700; + } + body aside h3::before { + content: '# '; + color: #00C16A; + } +` +document.head.appendChild(style) diff --git a/routers/home.js b/routers/home.js new file mode 100644 index 0000000..56004c9 --- /dev/null +++ b/routers/home.js @@ -0,0 +1 @@ +export default {} \ No newline at end of file diff --git a/server.js b/server.js index f904ee8..0a0576e 100644 --- a/server.js +++ b/server.js @@ -14,6 +14,15 @@ const databases = new Map() // 所有数据库 const FM = new HUB() // 频道消息分发器 const messagelist = new Map() // 消息队列的nedb存储 + +// 服务端推送 +// 1. 用户消息 +// 2. 系统消息 +// 3. 聊天消息 +// 4. 资源变化(观看中的资源变化)(预取集, 观看集, 缓存集) +// 5. 操作同步(其它用户的协作动作)(用户是临时附着与资源的对象, 由于其变化频繁且可丢弃, 通过会话通道读取) + + const db = (name) => (databases.get(name) || function () { let database = new nedb({ filename: `./data/db/${name}.db`, autoload: true, timestampData: true }) databases.set(name, database) @@ -25,15 +34,10 @@ 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 + if (req.session.account.gid !== 1) req.params.uid = req.session.account.uid // 普通用户只能操作自己的 next() } -// 权限(合并优化) -const admin = function (account, item) { - return (account.gid === 1 || account.uid === item.uid) -} - // 列表计量 const count_load = async (name, query) => await new Promise(resolve => db(name).count(query, (err, count) => resolve(count))) @@ -88,6 +92,7 @@ function session_create(req, res) { if (!doc) return res.status(400).send('账户不存在') return req.session.regenerate(function (err) { req.session.account = { uid: doc._id, gid: doc.gid ?? 0 } + req.session.isAdmin = () => (req.session.account.gid === 1) // 是否管理员 let { salt, password, ...user } = doc return res.json(user) }) @@ -199,6 +204,16 @@ const object_list = async function (req, res) { }) } +const isAdmin = (req) => req.session?.account?.gid === 1 + +const 权限模组 = (req, res, next) => { + // 创建任何对象, 如果不是管理员, 则不能设置 gid 和计算属性以及默认属性 + if (req.session?.account?.gid !== 1) { + ['_id', 'uid', 'top', 'user', 'createdAt', 'updatedAt', 'views', 'posts', 'likes', 'files'].forEach(i => delete req.body[i]) + } +} + + // 创建对象 const object_create = async function (req, res) { @@ -406,14 +421,6 @@ function index_get(req, res) { const app = expressWs(express()).app const ServerSentEventsClient = {} -// 开发模式下使用 Vite -if (process.argv.includes('--dev')) { - const vite = await createServer({ server: { middlewareMode: 'html' } }) - app.use(vite.middlewares) -} else { - app.use(express.static('dist')) -} - // Server-Sent Events (发送版本号来决定是否接收更新?) app.use((req, res, next) => { if (req.get('Accept') === 'text/event-stream') { @@ -463,4 +470,13 @@ app.route('/api/session').get(online, session_list).post(session_create).delete( app.route('/api/session/:sid').delete(online, session_delete) app.route('/api/:name').get(object_list).post(object_create).put(db_compact) app.route('/api/:name/:_id').get(object_load).post(online, file_upload).put().patch(online, object_patch).delete(online, object_remove) + +// 开发模式下使用 Vite +if (process.argv.includes('--dev')) { + const vite = await createServer({ server: { middlewareMode: 'html' } }) + app.use(vite.middlewares) +} else { + app.use(express.static('dist')) +} + app.listen(2333)