使用 Socket.IO 和 MongoDB 构建实时协作记事本

发布: (2026年1月3日 GMT+8 01:50)
4 min read
原文: Dev.to

Source: Dev.to

(抱歉,您只提供了来源链接,没有提供需要翻译的正文内容。请粘贴您想要翻译的文本,我会为您翻译成简体中文,并保留原始的 Markdown 格式、代码块和 URL。)

介绍

是否曾经需要快速与他人共享笔记,却被注册表单和复杂的权限设置弄得烦恼?Collaborative Notepad 是一个免费、开源的实时记事本,您可以创建笔记、分享链接,立即开始协作——无需账户。

功能

  • 无需注册
  • 通过 Socket.IO 实时协作
  • 自定义 URL(例如 /meeting-notes
  • 暗/亮模式切换
  • 每 3 秒自动保存
  • 实时用户计数显示

实时演示

https://collabnote.link

仓库

GitHub – collaborative‑notepad – MIT 许可证

Backend Stack

  • Node.js + Express
  • Socket.IO 用于实时通信
  • MongoDB 用于持久化
  • EJS 作为模板引擎
  • 安全性:Helmet、速率限制、CSP

服务器设置 (Express + Socket.IO)

// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const mongoose = require('mongoose');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

// MongoDB connection
mongoose.connect(process.env.MONGODB_URI);

// Socket.IO connection handling
io.on('connection', (socket) => {
  console.log('User connected');

  socket.on('join-note', async (noteId) => {
    socket.join(noteId);
    // Broadcast user count
    io.to(noteId).emit('user-count',
      io.sockets.adapter.rooms.get(noteId)?.size || 0
    );
  });

  socket.on('note-update', async ({ noteId, content }) => {
    // Save to database
    await Note.findOneAndUpdate(
      { noteId },
      { content, lastModified: Date.now() }
    );
    // Broadcast to all users in room
    socket.to(noteId).emit('note-change', content);
  });
});

MongoDB 模式

// models/Note.js
const noteSchema = new mongoose.Schema({
  noteId: { type: String, unique: true, required: true },
  content: { type: String, default: '' },
  createdAt: { type: Date, default: Date.now },
  lastModified: { type: Date, default: Date.now },
  viewCount: { type: Number, default: 0 }
});

const Note = mongoose.model('Note', noteSchema);

客户端实时同步

// public/js/app.js
const socket = io();
const noteId = window.location.pathname.substring(1) || 'home';
const textarea = document.getElementById('note');
const userCountElement = document.getElementById('user-count');

// Join note room
socket.emit('join-note', noteId);

// Send updates to server (debounced)
let timeout;
textarea.addEventListener('input', () => {
  clearTimeout(timeout);
  timeout = setTimeout(() => {
    socket.emit('note-update', {
      noteId,
      content: textarea.value
    });
  }, 300);
});

// Receive updates from other users
socket.on('note-change', (content) => {
  if (document.activeElement !== textarea) {
    textarea.value = content;
  }
});

// Update user count
socket.on('user-count', (count) => {
  userCountElement.textContent = count;
});

限流

// middleware/rateLimit.js
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per window
});

app.use('/api/', limiter);

内容安全策略

// security/csp.js
const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"],
    styleSrc: ["'self'", "'unsafe-inline'"]
  }
}));

XSS 防护

const xss = require('xss');

// Example sanitization
const sanitizedContent = xss(userInput);

Express 路由自定义 URL

app.get('/:noteId', async (req, res) => {
  const noteId = req.params.noteId;
  const note = await Note.findOne({ noteId }) ||
               await Note.create({ noteId });

  res.render('index', { note });
});

自动保存逻辑

let saveTimeout;
const AUTO_SAVE_DELAY = 3000; // 3 seconds

textarea.addEventListener('input', () => {
  clearTimeout(saveTimeout);
  saveTimeout = setTimeout(saveToDatabase, AUTO_SAVE_DELAY);
});

主题(CSS 自定义属性)

:root {
  --bg-color: #fefae0;
  --text-color: #1e3a1f;
}

[data-theme="dark"] {
  --bg-color: #1e1e1e;
  --text-color: #e0e0e0;
}

性能与可靠性优化

  • Debouncing socket 事件,以降低服务器负载
  • 用于快速笔记查找的 MongoDB indexing
  • 用于高效数据库使用的 Connection pooling
  • 用于更快资源交付的 Gzip compression 中间件
  • 用于将每个笔记的协作隔离的 Socket.IO rooms

未来改进(路线图)

  • Markdown 支持
  • 代码块语法高亮
  • 密码保护的笔记
  • 导出为 PDF/TXT
  • 协作光标位置
  • 语音笔记
  • 移动应用

技术栈概览

  • Node.js
  • Express
  • Socket.IO
  • MongoDB
  • EJS

开发时间

≈ 20 小时

许可证

MIT(开源)


如果您觉得这有帮助,请点个 ❤️ 并给 GitHub 仓库加星!

Back to Blog

相关文章

阅读更多 »