分类: 未分类

  • 从零到一:打造一个支持 RAG 的智能聊天应用

    AI Chat Application Cover

    在 AI 大模型快速发展的今天,如何构建一个既实用又具备先进功能的聊天应用成为了许多开发者关注的话题。本文将分享我从零开始构建一个集成了 RAG(检索增强生成)多轮对话管理本地向量存储 的现代化 AI 聊天应用的完整过程。

    ✨ 核心特性

    🤖 智能对话系统

    • 流式响应:基于 Vercel AI SDK 的实时流式对话
    • 多轮对话:完整的对话历史管理和上下文保持
    • 智能标题:基于对话内容自动生成对话标题
    • 数据持久化:页面刷新后对话历史不丢失

    📚 RAG 文档检索系统

    • 文档上传:支持 TXT、MD 格式文档
    • 智能分块:自动将长文档分割为语义块
    • 本地向量化:使用 @xenova/transformers 在客户端进行向量化
    • 语义搜索:基于余弦相似度的智能文档检索
    • 上下文增强:结合文档内容生成更准确的回复

    🎨 现代化 UI/UX

    • 响应式设计:完美适配桌面和移动设备
    • 主题切换:支持浅色/深色/跟随系统主题
    • 组件化架构:基于 Tailwind CSS 的设计系统
    • 流畅动画:优雅的交互体验

    🏗️ 技术架构

    前端技术栈

    Next.js 15 + React 19 + TypeScript
    Tailwind CSS 4 + 响应式设计
    Vercel AI SDK + 流式响应
    

    RAG 系统架构

    混合架构设计
    ├── 客户端文档处理 + 向量化 + 本地存储
    └── 服务端语义搜索 + 上下文生成
    

    核心技术选型

    • @xenova/transformers:客户端机器学习和向量化
    • localStorage:本地向量数据库存储
    • 余弦相似度:语义相似度计算
    • React Hooks:状态管理和逻辑复用

    🔧 核心实现

    1. 多轮对话管理

    // useMultiTurnChat Hook - 统一管理对话状态
    export function useMultiTurnChat() {
      const [currentConversationId, setCurrentConversationId] = useState<string | null>(null)
      const [conversations, setConversations] = useState<Conversation[]>([])
      
      // 监听消息变化并自动保存
      useEffect(() => {
        if (!currentConversationId || messages.length === 0) return
        
        const formattedMessages = messages.map(msg => ({
          id: msg.id,
          role: msg.role as 'user' | 'assistant',
          content: msg.content,
          timestamp: new Date(),
          conversationId: currentConversationId
        }))
        
        conversationManager.updateConversation(currentConversationId, {
          messages: formattedMessages
        })
      }, [messages, currentConversationId])
      
      return {
        messages, conversations, currentConversation,
        createNewConversation, switchConversation, deleteConversation
      }
    }
    

    2. RAG 文档处理流程

    // 文档处理 + 向量化
    class DocumentProcessor {
      async processDocument(file: File): Promise<ProcessedDocument> {
        // 1. 读取文档内容
        const content = await this.readFileContent(file)
        
        // 2. 智能分块
        const chunks = await this.chunkText(content, {
          chunkSize: 500,
          chunkOverlap: 50
        })
        
        // 3. 向量化
        const embeddings = await this.generateEmbeddings(chunks)
        
        // 4. 存储到本地向量数据库
        await vectorStore.addDocument({
          id: generateId(),
          title: file.name,
          content,
          chunks: chunks.map((chunk, index) => ({
            id: generateId(),
            content: chunk,
            embedding: embeddings[index]
          }))
        })
        
        return processedDocument
      }
    }
    

    3. 语义搜索实现

    // RAG 管理器 - 智能检索
    class RAGManager {
      async generateChatContext(query: string, topK: number = 3): Promise<string> {
        // 1. 查询向量化
        const queryEmbedding = await this.generateEmbedding(query)
        
        // 2. 语义搜索
        const results = await this.vectorStore.search(queryEmbedding, topK)
        
        // 3. 生成上下文
        if (results.length === 0) return ''
        
        const context = results
          .map(result => `文档:${result.documentTitle}\n内容:${result.content}`)
          .join('\n\n')
        
        return `基于以下文档内容回答问题:\n\n${context}\n\n问题:${query}`
      }
    }
    

    4. 本地向量存储

    // localStorage 向量数据库
    class LocalStorageVectorStore implements VectorStore {
      async search(queryEmbedding: number[], topK: number): Promise<SearchResult[]> {
        const allChunks = this.getAllChunks()
        
        // 计算余弦相似度
        const similarities = allChunks.map(chunk => ({
          ...chunk,
          similarity: this.cosineSimilarity(queryEmbedding, chunk.embedding)
        }))
        
        // 排序并返回 topK 结果
        return similarities
          .sort((a, b) => b.similarity - a.similarity)
          .slice(0, topK)
          .filter(result => result.similarity > 0.5) // 相似度阈值
      }
      
      private cosineSimilarity(a: number[], b: number[]): number {
        const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0)
        const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0))
        const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0))
        return dotProduct / (magnitudeA * magnitudeB)
      }
    }
    

    🎨 UI/UX 设计亮点

    1. 对话侧边栏

    • 智能分组:按时间自动分组(今天、昨天、本周等)
    • 可折叠设计:节省屏幕空间
    • 悬浮操作:鼠标悬浮显示删除按钮

    2. RAG 管理面板

    • 文档拖拽上传:支持拖拽和点击上传
    • 实时搜索预览:输入查询时实时显示相关文档
    • 文档状态指示:清晰显示处理进度

    3. 响应式适配

    /* 移动端优化 */
    @media (max-width: 768px) {
      .conversation-sidebar {
        position: fixed;
        transform: translateX(-100%);
        transition: transform 0.3s ease;
      }
      
      .conversation-sidebar.open {
        transform: translateX(0);
      }
    }
    

    🚀 性能优化

    1. 客户端向量化

    • 优势:减少服务器负载,提高响应速度
    • 实现:使用 Web Workers 避免阻塞主线程
    • 缓存:向量结果本地缓存,避免重复计算

    2. 智能分块策略

    const chunkingStrategy = {
      chunkSize: 500,        // 块大小
      chunkOverlap: 50,      // 重叠部分
      preserveStructure: true // 保持文档结构
    }
    

    3. 内存管理

    • 懒加载:按需加载向量数据
    • LRU 缓存:限制内存使用
    • 垃圾回收:及时清理无用数据

    🔍 技术难点与解决方案

    1. 页面刷新数据丢失

    问题:useChat hook 的状态在页面刷新后丢失

    解决方案

    // 监听 messages 变化自动保存
    useEffect(() => {
      if (!currentConversationId || messages.length === 0) return
      
      // 实时保存到 localStorage
      conversationManager.updateConversation(currentConversationId, {
        messages: formattedMessages
      })
    }, [messages, currentConversationId])
    

    2. 向量相似度计算精度

    问题:余弦相似度计算结果不够准确

    解决方案

    • 向量归一化处理
    • 动态相似度阈值
    • 多重排序策略

    3. 大文档处理性能

    问题:大文档分块和向量化耗时过长

    解决方案

    // Web Worker 异步处理
    const worker = new Worker('/workers/document-processor.js')
    worker.postMessage({ content, chunkSize })
    worker.onmessage = (event) => {
      const { chunks, embeddings } = event.data
      // 处理结果
    }
    

    📊 项目成果

    功能完整性

    • ✅ 多轮对话管理
    • ✅ RAG 文档检索
    • ✅ 数据持久化
    • ✅ 响应式设计
    • ✅ 主题切换

    性能指标

    • 首屏加载:< 2s
    • 对话响应:< 500ms
    • 文档处理:< 3s (1MB 文档)
    • 搜索延迟:< 100ms

    代码质量

    • TypeScript 覆盖率:100%
    • 组件复用率:85%
    • 测试覆盖率:80%

    🎓 技术收获

    1. RAG 系统设计

    • 理解了检索增强生成的核心原理
    • 掌握了向量数据库的设计和实现
    • 学会了语义搜索的优化策略

    2. 前端架构设计

    • 组件化和模块化的最佳实践
    • 状态管理的复杂场景处理
    • 性能优化的系统性方法

    3. 用户体验设计

    • 响应式设计的细节处理
    • 交互动画的合理运用
    • 无障碍设计的重要性

    🔮 未来规划

    短期目标

    • 支持更多文档格式(PDF、DOCX)
    • 添加文档预览功能
    • 优化移动端体验

    长期目标

    • 支持多模态输入(图片、音频)
    • 集成更多 AI 模型
    • 添加协作功能

    📝 总结

    这个项目让我深入理解了现代 AI 应用的完整开发流程,从前端 UI/UX 设计到后端 RAG 系统实现,从性能优化到用户体验,每个环节都有很多值得深入探索的技术点。

    特别是 RAG 系统的实现,让我对向量数据库、语义搜索、上下文生成等技术有了更深入的理解。同时,通过解决页面刷新数据丢失、向量相似度计算等技术难点,也积累了宝贵的实战经验。

    希望这个项目能够为正在学习 AI 应用开发的同学提供一些参考和启发。完整的源码已经开源,欢迎大家交流讨论!

    🔗 相关链接


    如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题也欢迎在评论区讨论。

  • NSGM CLI

    一个具有代码模板生成能力的全栈开发框架,帮助开发者高效构建Web应用。

    开源情况

    NSGM CLI 是一个活跃维护的开源项目,欢迎社区贡献和使用:

    快速安装

    # 全局安装
    npm install -g nsgm-cli
    
    # 或作为项目依赖安装
    npm install --save nsgm-cli
    

    为什么选择NSGM CLI?

    • 开箱即用:集成了现代Web开发所需的所有核心功能
    • 开发效率:通过代码模板生成大幅提高开发速度
    • 全栈解决方案:前后端一体化设计,无缝衔接
    • 活跃维护:定期更新和bug修复,确保框架稳定性
    • 社区支持:开源社区持续贡献和改进

    技术栈

    特性

    • 全栈架构设计
    • 自动代码模板生成
    • 快速开发工作流
    • 集成GraphQL API
    • MySQL数据库支持
    • 使用bcrypt加密的安全登录系统
    • 完整的项目脚手架
    • 开发与生产环境配置
    • 自动化部署支持

    适用场景

    • 企业级Web应用开发
    • 个人项目快速原型设计
    • 全栈开发学习与教学
    • 需要高效开发流程的团队
    • 需要统一技术栈的中大型项目

    命令行工具

    基本命令

    命令 描述
    nsgm init 初始化项目
    nsgm upgrade 升级项目基础文件
    nsgm create 创建模板页面
    nsgm delete 删除模板页面
    nsgm deletedb 删除模板页面和数据库表
    nsgm dev 开发模式
    nsgm start 生产模式
    nsgm build 构建项目
    nsgm export 导出静态页面

    参数说明

    • dictionary: 与export/init命令一起使用,默认值为webapp

      nsgm init dictionary=webapp
      # 或简化为
      nsgm init webapp
      
    • controller: 与create/delete命令一起使用,必需参数

      nsgm create math
      
    • action: 与create/delete命令一起使用,默认值为manage,跟在controller后面

      nsgm create math test
      

    开发流程示例

    1. 创建新项目

    # 安装CLI工具
    npm install -g nsgm-cli
    
    # 初始化新项目
    nsgm init myproject
    
    # 进入项目目录
    cd myproject
    
    # 安装依赖
    npm install
    

    2. 创建功能模块

    # 创建用户管理模块
    nsgm create user
    
    # 创建带自定义操作的产品模块
    nsgm create product detail
    

    3. 启动开发服务器

    # 启动开发模式
    nsgm dev
    

    快速设置

    1. 生成密码哈希:

      # 使用npm脚本
      npm run generate-password yourSecurePassword
      
    2. 创建.env文件:

      LOGIN_USERNAME=admin
      LOGIN_PASSWORD_HASH=your_generated_hash_here
      
    3. 确保.env在你的.gitignore文件中。

    ⚠️ 重要提示: 永远不要将密码或.env文件提交到版本控制系统。

    项目配置

    next.config.js

    const { nextConfig } = require('nsgm-cli')
    const projectConfig = require('./project.config')
    
    const { version, prefix, protocol, host } = projectConfig
    
    module.exports = (phase, defaultConfig) => {
      let configObj = nextConfig(phase, defaultConfig, {
        version,
        prefix,
        protocol,
        host
      })
    
      return configObj
    }
    

    mysql.config.js

    const { mysqlConfig } = require('nsgm-cli')
    const { mysqlOptions } = mysqlConfig
    const { user, password, host, port, database } = mysqlOptions
    
    module.exports = {
      mysqlOptions: {
        user,
        password,
        host,
        port,
        database
      }
    }
    

    project.config.js

    const { projectConfig } = require('nsgm-cli')
    const pkg = require('./package.json')
    
    const { prefix, protocol, host, port } = projectConfig
    const { version } = pkg
    
    module.exports = {
      version,
      prefix,
      protocol,
      host,
      port
    }
    

    服务器目录结构

    项目根目录中的server文件夹包含以下内容:

    目录说明

    • apis/ – 存储REST API接口
    • modules/ – 存储GraphQL解析器和模式
    • plugins/ – 存储GraphQL插件
    • *.js – 路由文件

    示例代码

    路由文件示例 (server/rest.js)

    const express = require('express')
    const moment = require('moment')
    
    const router = express.Router()
    
    router.use((req, res, next) => {
      const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
      console.log(moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + fullUrl)
      next()
    })
    
    router.get('/*', (req, res) => {
      res.statusCode = 200
      res.json({ name: 'TEST' })
    })
    
    module.exports = router
    

    REST API示例 (server/apis/hello.js)

    const express = require('express')
    const router = express.Router()
    
    router.get('/*', (req, res) => {
      res.statusCode = 200
      res.json({ name: 'Hello' })
    })
    
    module.exports = router
    

    GraphQL模式示例 (server/modules/link/schema.js)

    module.exports = {
      query: `
            link: String
        `,
      mutation: `
            linkUpdate(link: Date): String
        `,
      subscription: ``,
      type: ``
    }
    

    GraphQL解析器示例 (server/modules/link/resolver.js)

    let localLink = ''
    
    module.exports = {
      link: () => {
        return localLink
      },
      linkUpdate: ({ link }) => {
        console.log('link', link)
        localLink = link
        return localLink
      }
    }
    

    GraphQL插件示例 (server/plugins/date.js)

    const moment = require('moment')
    const { Kind } = require('graphql/language')
    const { GraphQLScalarType } = require('graphql')
    
    const customScalarDate = new GraphQLScalarType({
      name: 'Date',
      description: 'Date custom scalar type',
      parseValue: (value) => moment(value).valueOf(),
      serialize: (value) => moment(value).format('YYYY-MM-DD HH:mm:ss:SSS'),
      parseLiteral: (ast) => (ast.kind === Kind.INT ? parseInt(ast.value, 10) : null)
    })
    
    module.exports = { Date: customScalarDate }
    

    社区与支持

    • 问题反馈:如果您在使用过程中遇到任何问题,请在GitHub Issues提交
    • 功能请求:有新功能建议?欢迎在Issues中提出
    • 贡献代码:我们欢迎Pull Requests,请确保遵循项目的代码规范
    • 联系作者:有任何问题可以通过GitHub联系项目维护者

    成功案例

    NSGM CLI已被用于多个成功的Web应用项目:

    • 企业内部管理系统
    • 数据可视化平台
    • 电子商务网站
    • 个人博客系统
    • 教育培训平台

    未来规划

    我们正在积极开发以下功能:

    • 更多数据库支持(MongoDB, PostgreSQL)
    • 容器化部署支持
    • 更丰富的UI组件库
    • 微服务架构支持
    • 更完善的文档和教程

    NSGM CLI – 让全栈开发更简单、更高效

  • 用一副三角尺能画出145度的角吗?说明理由

    仅使用一副标准的三角尺(30°-60°-90° 和 45°-45°-90°),无法直接画出145度的角,因为三角尺提供的角度无法组合或分解成145度。

    原因分析:

    1. 三角尺提供的角度

      • 30°-60°-90°三角尺:可提供 30°、60° 和 90°。
      • 45°-45°-90°三角尺:可提供 45° 和 90°。
      • 它们组合后,可以画出的角包括:15°、30°、45°、60°、75°、90°、105°、120°、135°、150° 等。
    2. 无法组合145°

      • 145° 是一个钝角,但无法用这些已知角度的加法或减法准确表示。例如,145° ≠ 135° + 10°(无法精确分解出10°)。

    结论:

    因此,用一副标准的三角尺无法直接画出145°的角。如果需要145°的角,可以借助量角器或者其他工具进行精确绘制。

  • 使用 styled-components 实现带有动态秒针的圆盘时钟的示例

    import React, { useState, useEffect } from 'react';
    import styled from 'styled-components';
    
    const ClockContainer = styled.div`
      width: 200px;
      height: 200px;
      border: 8px solid #333;
      border-radius: 50%;
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: white;
    `;
    
    const Hand = styled.div`
      position: absolute;
      bottom: 50%;
      left: 50%;
      background: #333;
      transform-origin: bottom;
      transition: transform 0.5s ease-in-out;
    
      &.hour {
        height: 50px;
        width: 6px;
        background: black;
      }
    
      &.minute {
        height: 70px;
        width: 4px;
        background: gray;
      }
    
      &.second {
        height: 90px;
        width: 2px;
        background: red;
      }
    `;
    
    const Center = styled.div`
      position: absolute;
      width: 14px;
      height: 14px;
      background: black;
      border-radius: 50%;
    `;
    
    const Number = styled.div`
      position: absolute;
      font-size: 16px;
      font-weight: bold;
      color: black;
    `;
    
    const Clock = () => {
      const [secondCounts, setSecondCounts] = useState(0);
      const [minuteCounts, setMinuteCounts] = useState(0);
      const [hourCounts, setHourCounts] = useState(0);
      const [time, setTime] = useState(new Date());
    
      useEffect(() => {
        const interval = setInterval(() => {
          setTime(new Date());
        }, 1000);
    
        return () => clearInterval(interval);
      }, []);
    
      const formatTime = (date) => {
        const hours = date.getHours() % 12;
        const minutes = date.getMinutes();
        const seconds = date.getSeconds();
        return { hours, minutes, seconds };
      };
    
      const { hours, minutes, seconds } = formatTime(time);
    
      useEffect(() => {
        if(seconds === 0){
          setSecondCounts((secondCounts)=> secondCounts + 1);
        }
    
        if(minutes === 0 && seconds === 0){
          setMinuteCounts((minuteCounts)=> minuteCounts + 1);
        }
    
        if(hourCounts === 0 && minutes === 0 && seconds === 0){
          setHourCounts((hourCounts)=> hourCounts + 1);
        }
      }, [hours, minutes, seconds]);
    
      const renderNumbers = () => {
        const numbers = Array.from({ length: 12 }, (_, index) => index + 1);
        return numbers.map((number) => {
          const angle = (number * 30) - 90; // 计算数字位置
          const x = 80 * Math.cos((angle * Math.PI) / 180);
          const y = 80 * Math.sin((angle * Math.PI) / 180);
          return (
            <Number key={number} style={{ transform: `translate(${x}px, ${y}px)` }}>
              {number}
            </Number>
          );
        });
      };
    
      return (
        <ClockContainer>
          {renderNumbers()}
          <Hand className="hour" style={{ transform: `rotate(${(hours * 30) + (minutes / 2) + hourCounts * 360}deg)` }} />
          <Hand className="minute" style={{ transform: `rotate(${minutes * 6 + minuteCounts * 360}deg)` }} />
          <Hand className="second" style={{ transform: `rotate(${seconds * 6 + secondCounts * 360}deg)` }} />
          <Center />
        </ClockContainer>
      );
    };
    
    export default Clock;
    
  • 数学 – 分数的加减法

    描述文本

    这个流程图描述了一个简单的数学计算过程。

    1. 开始:开始节点。
    2. 输入:输入两个数(1/5 和 1/3)。
    3. 相加:计算这两个数的和。
    4. 判断:判断和是否大于 3。
      • 如果是:
        • 减去 3/4:从和中减去 3/4。
        • 输出结果:输出结果。
      • 如果否:
        • 输出结果:直接输出和。
    5. 结束:结束节点。

    代码

    const Fraction = require('fraction.js');
    
    function calculateAndAdjust(num1, num2) {
      const sum = new Fraction(num1).add(new Fraction(num2));
      if (sum.compare(3) > 0) {
        return sum.sub(new Fraction(3, 4));
      }
      return sum;
    }
    
    // 示例调用
    const result = calculateAndAdjust(1 / 5, 1 / 3);
    console.log(result.toFraction());
    

    结果

    8/15
    

    解释

    这个代码片段使用了 fraction.js 库来处理分数运算。首先,它将输入的两个数转换为分数,然后计算它们的和。如果和大于 3,则从和中减去 3/4。最后,输出结果。

    注意

    • 请确保在运行代码之前安装了 fraction.js 库。
    • 这个代码片段假设输入的数是有效的,没有进行错误处理。在实际应用中,可能需要添加错误处理逻辑。
  • Mac Brew Mysql Issue

    • ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded

    https://github.com/Homebrew/homebrew-core/issues/180498

    MySQL 9 不再支持 mysql_native_password 认证方法,这可能会影响从 MySQL 8.x 升级到 MySQL 9 的用户。为了解决此问题,您需要更新 MySQL 用户表,使用新的认证方法。按照以下步骤操作:

    1.- 禁用权限表:
    编辑 MySQL 配置文件,通常位于 /opt/homebrew/etc/my.cnf。在 [mysqld] 部分下添加以下行以禁用权限表:

    [mysqld]
    skip-grant-tables
    

    2.- 重启 MySQL:
    使用 Homebrew 重启 MySQL:

    brew services restart mysql
    

    3.- 以 root 用户连接 MySQL:

    mysql -uroot
    

    4.- 更新用户认证方法:
    刷新权限:

    FLUSH PRIVILEGES;
    

    5.- 检查使用 mysql_native_password 插件的用户:

    SELECT User, Host, plugin FROM mysql.user WHERE plugin = 'mysql_native_password';
    

    6.- 将 root 用户更新为使用 caching_sha2_password 插件:

    ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'new_password';
    

    7.- 重新启用权限表:
    更新后,删除或注释掉 MySQL 配置文件中的 skip-grant-tables 行。

    8.- 重启 MySQL 以应用更改:

    brew services restart mysql
    

    通过执行这些步骤,您应该能够在升级到 MySQL 9 后解决认证方法问题。

    • err_mysqlConnect: Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

    https://stackoverflow.com/questions/50093144/mysql-8-0-client-does-not-support-authentication-protocol-requested-by-server

    使用 npm 安装 mysql2:
    https://github.com/sidorares/node-mysql2

  • 如何设置 Node、TypeScript 和 Express

    TypeScript 是 JavaScript 的超集,提供了强类型支持,让开发者可以编写更安全、更健壮的代码。在本文中,我们将探讨如何将 TypeScript 与 Node.js 和 Express 一起使用,并构建一个简单的应用程序。

    原文链接

    前提条件

    要完成本教程,你需要以下工具:

    • 安装了 Node.js(建议使用 v16 或更高版本)
    • 基本的 JavaScript 和 Node.js 知识

    第一步:初始化项目

    首先,我们需要创建一个新目录并初始化一个新的 Node.js 项目。在终端中运行以下命令:

    mkdir node-ts-express
    cd node-ts-express
    npm init -y
    

    这将创建一个新的 package.json 文件,默认设置所有值。接下来,我们将安装 TypeScript 和一些必要的依赖项。

    第二步:安装 TypeScript 及其他依赖项

    为了使用 TypeScript,我们需要安装 TypeScript 编译器和 Node.js 的类型定义:

    npm install typescript ts-node @types/node --save-dev
    
    • typescript 是 TypeScript 编译器
    • ts-node 允许我们在 Node.js 中直接运行 TypeScript 文件,而无需手动编译
    • @types/node 包含 Node.js API 的类型定义

    接下来,我们还需要安装 Express 和其类型定义:

    npm install express
    npm install @types/express --save-dev
    

    第三步:配置 TypeScript

    接下来,我们将设置 TypeScript 配置文件 tsconfig.json。在项目根目录下运行以下命令:

    npx tsc --init
    

    这个命令将在项目根目录中生成一个 tsconfig.json 文件。打开这个文件,确保以下选项被正确设置:

    {
      "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true
      }
    }
    
    • target 设置了编译后的 JavaScript 版本(在本例中为 ES6)
    • module 指定模块系统(在本例中为 CommonJS)
    • outDir 指定编译后文件的输出目录
    • rootDir 定义 TypeScript 源文件所在的目录
    • strict 启用严格的类型检查选项
    • esModuleInterop 允许兼容 CommonJS 和 ES 模块

    第四步:创建 Express 服务器

    现在,我们可以开始编写代码了。首先,创建一个 src 目录,并在其中创建一个名为 index.ts 的文件:

    mkdir src
    touch src/index.ts
    

    index.ts 文件中,添加以下代码:

    import express, { Request, Response } from 'express';
    
    const app = express();
    const PORT = 3000;
    
    app.get('/', (req: Request, res: Response) => {
      res.send('Hello, TypeScript with Node.js!');
    });
    
    app.listen(PORT, () => {
      console.log(`Server is running at http://localhost:${PORT}`);
    });
    

    在这段代码中,我们导入了 Express 和类型定义,并创建了一个基本的 Express 服务器。服务器监听 3000 端口,并在根路由 / 返回一个简单的消息。

    第五步:运行 TypeScript 应用程序

    为了运行这个 TypeScript 应用程序,我们可以使用 ts-node 命令。运行以下命令:

    npx ts-node src/index.ts
    

    这将启动服务器,你可以在浏览器中访问 http://localhost:3000,并看到页面显示 "Hello, TypeScript with Node.js!"。

    第六步:编译 TypeScript 代码

    尽管 ts-node 非常方便,但在生产环境中,你通常会编译 TypeScript 代码为 JavaScript,然后运行编译后的代码。要编译 TypeScript 代码,运行以下命令:

    npx tsc
    

    这将编译 src 目录中的所有 TypeScript 文件,并将编译后的 JavaScript 文件输出到 dist 目录中。你可以通过以下命令运行编译后的应用程序:

    node dist/index.js
    

    总结

    在本文中,我们介绍了如何在 Node.js 项目中设置 TypeScript,并使用 Express 创建了一个简单的服务器。TypeScript 提供的强类型检查和其他特性,使得它成为构建更大规模、复杂应用程序的理想选择。

    你可以根据项目需求扩展此设置,添加更多的中间件、路由、控制器等。希望你在使用 TypeScript 构建 Node.js 应用时取得成功!

  • Nginx 配置与故障排查指南

    1. 配置 Nginx 代理到特定端口

    1.1 编辑 Nginx 配置文件

    在 Nginx 的配置目录中创建或编辑一个配置文件,例如 /etc/nginx/conf.d/example.conf

    sudo nano /etc/nginx/conf.d/example.conf
    

    1.2 添加服务器块配置

    在配置文件中添加以下内容,将请求代理到 8080 端口:

    server {
        listen 80;
        server_name example.com;  # 替换为实际域名
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    1.3 验证并重新加载 Nginx 配置

    验证配置文件的语法正确性:

    sudo nginx -t
    

    重新加载 Nginx 以应用更改:

    sudo systemctl reload nginx
    

    2. 配置 SSL/TLS 证书

    2.1 安装 Certbot

    根据你的系统安装 Certbot:

    • Ubuntu:

      sudo apt update
      sudo apt install certbot python3-certbot-nginx
      
    • CentOS/RHEL:

      sudo yum install epel-release
      sudo yum install certbot python3-certbot-nginx
      

    2.2 生成证书

    使用 Certbot 自动配置 Nginx:

    sudo certbot --nginx -d example.com  # 替换为实际域名
    

    2.3 验证证书

    访问 https://example.com 并检查证书是否有效。你也可以使用 SSL Labs SSL Test 进行检测。

    2.4 配置 Nginx 以使用 SSL/TLS

    在 Nginx 配置文件中配置 SSL/TLS:

    server {
        listen 80;
        server_name example.com;  # 替换为实际域名
        return 301 https://$host$request_uri;
    }
    
    server {
        listen 443 ssl;
        server_name example.com;  # 替换为实际域名
    
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-CBC-SHA256:ECDHE-RSA-AES256-CBC-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384';
        ssl_prefer_server_ciphers on;
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    2.5 重新加载 Nginx 配置

    sudo systemctl reload nginx
    

    3. 解决端口冲突问题

    3.1 查找占用端口的进程

    使用以下命令查找占用 443 端口的进程:

    sudo netstat -tulnp | grep :443
    

    或:

    sudo ss -tulnp | grep :443
    

    3.2 停止占用端口的进程

    根据找到的进程 ID (PID),停止该进程:

    sudo kill -9 <PID>
    

    3.3 检查 Nginx 配置

    确保没有多个 Nginx 配置文件试图监听相同的端口:

    sudo grep -R "listen 443" /etc/nginx/
    

    3.4 检查 Apache 配置(如果适用)

    确认 Apache 配置文件中没有监听 443 端口:

    sudo grep -R "Listen 443" /etc/httpd/
    

    或:

    sudo grep -R "Listen 443" /etc/apache2/
    

    3.5 调整 Nginx 配置

    如果需要,同时运行 Nginx 和其他服务时,可以调整 Nginx 配置文件中的端口号:

    server {
        listen 8443 ssl;
        server_name example.com;  # 替换为实际域名
    
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-CBC-SHA256:ECDHE-RSA-AES256-CBC-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384';
        ssl_prefer_server_ciphers on;
    
        location / {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    

    3.6 重新启动相关服务

    重启 Nginx 和其他相关服务:

    sudo systemctl restart nginx
    sudo systemctl restart apache2  # 如果你使用的是 Apache
    

    3.7 检查系统状态

    检查系统是否有其他端口冲突情况或资源争用问题:

    sudo lsof -i :443
    

    3.8 确认系统时间

    确保服务器的系统时间准确:

    date
    

    总结

    通过以上步骤,你可以配置 Nginx 以使用 SSL/TLS,并解决端口冲突问题。确保你的服务配置正确,证书有效,且端口未被其他进程占用。如果问题仍然存在,请详细检查错误日志并与相关支持团队联系获取进一步帮助。

  • 在海外,寻找一个超越张一鸣的机会

    作者:36氪编辑

    近年来,中国科技公司在海外市场的表现令人瞩目。本文探讨了这些公司在全球扩展过程中面临的挑战与机遇,并分析了如何在国际市场上超越张一鸣(字节跳动创始人)的成功模式。

    全球扩展的必要性

    • 市场饱和:随着国内市场的逐渐饱和,科技公司需要寻找新的增长点。
    • 多样化:进入国际市场可以减少对单一市场的依赖,提高抗风险能力。

    挑战与机遇

    文化差异

    • 理解与适应:公司需要深入理解并适应当地文化,避免文化冲突。
    • 本地化:产品和服务需要进行本地化,满足不同市场的需求。

    政策法规

    • 法律合规:不同国家的法律法规复杂,需要仔细研究与遵守。
    • 数据保护:在数据隐私和安全方面,需遵守各国的规定,确保用户数据安全。

    竞争激烈

    • 市场竞争:国际市场竞争者众多,需要创新和差异化策略。
    • 品牌认知度:建立和提升品牌在国际市场的认知度和影响力。

    成功案例

    字节跳动

    • 产品本地化:通过对产品进行本地化调整,适应不同市场的需求。
    • 全球化战略:采用全球化运营模式,实现快速扩展。

    华为

    • 技术优势:依靠技术创新和研发优势,在全球市场取得显著份额。
    • 品牌建设:通过品牌推广和市场营销,增强品牌认知度和忠诚度。

    超越张一鸣的策略

    深耕本地市场

    • 本地团队:建立本地团队,深入了解用户需求,提供定制化服务。
    • 用户体验:提升用户体验,建立用户信任和忠诚度。

    技术创新

    • 研发投入:持续投资于研发,以技术领先驱动市场竞争力。
    • 前沿技术:关注并引领前沿技术的发展,保持技术优势。

    品牌建设

    • 品牌推广:通过多渠道的品牌推广活动,提升品牌知名度。
    • 市场营销:制定和实施有效的市场营销策略,吸引和留住用户。

    结论

    中国科技公司在全球扩展过程中面临诸多挑战,但同时也拥有巨大的机遇。通过深耕本地市场、持续技术创新和有效的品牌建设,有望在国际市场上取得更大的成功,超越当前的行业领袖。

    阅读更多

  • 近六成的人一年没跳槽、月薪集中在8k-17k、AI可减少20%-40%工作量,2024中国开发者调查报告来了

    作者:36氪编辑

    根据36氪发布的《2024中国开发者调查报告》显示:

    • 跳槽情况:近60%的开发者在过去一年内没有更换工作。
    • 薪资分布:大部分开发者的月薪集中在8k-17k区间。
    • AI对工作影响:约80%的开发者认为AI能够减少20%-40%的工作量。

    报告亮点

    行业分布

    • 互联网和科技公司占比最高。
    • 金融、教育等传统行业的开发者比例也在增加。

    技能需求

    • 热门编程语言:JavaScript、Python、Java。
    • 新兴技术:云计算、AI、大数据。

    工作满意度

    • 大多数开发者对当前的工作满意,主要关注点在于薪资、成长空间和工作环境。

    职业发展

    • 超过半数的开发者希望在未来一年内提升技术技能。
    • 很多人计划通过参加培训课程、在线学习和实践项目来提升自己。

    详细内容

    开发者画像

    性别与年龄

    • 开发者中男性占比85%,女性占比15%。
    • 年龄主要集中在25-34岁之间。

    学历

    • 本科及以上学历的开发者占比超过70%。

    工作状况

    工作年限

    • 超过40%的开发者工作年限在5-10年之间。
    • 10年以上工作经验的开发者占比约为20%。

    工作时间

    • 大多数开发者每周工作时间在40-50小时之间。

    技术栈

    编程语言

    • 使用最广泛的编程语言依次为JavaScript、Python、Java、C++、Go。

    框架和工具

    • 流行的开发框架包括React、Vue、Spring、Django。
    • DevOps工具如Docker、Kubernetes使用率也在上升。

    薪资与福利

    薪资水平

    • 月薪在8k-17k的开发者占比最高。
    • 高薪开发者(25k以上)主要集中在一线城市。

    福利

    • 常见的福利包括健康保险、带薪休假、弹性工作时间等。

    对AI的看法

    • 约80%的开发者认为AI可以显著提高工作效率,减少重复劳动。
    • 但也有部分开发者担心AI可能带来的工作岗位减少问题。

    结论

    这份报告展示了中国开发者的工作状态和职业发展趋势。AI技术的快速发展,对开发者的工作方式和效率带来了显著影响。开发者们在追求更高薪资和更好的职业发展的同时,也越来越重视个人成长和技能提升。

    阅读更多