notes 笔记notes 笔记
Home
Article
Category
Tag
Timeline
Home
Article
Category
Tag
Timeline

Category: Tag:

技术六大类

基础
编程语言
数据库
接口
服务和托管
运维和部署

具体有: 数据库。 jmysql、mongodb、postgresql、oracle、Redis等 Elasticsearch kafka/RabbitMQ/RocketMQ/Pulsar

语言
javascript、python、java、php、c#、go、rust等

APIS&WebServices
APIS类型:rest/graphql/soap/grpc
认证及安全:jwt/oauth2/API Keys


Category: Tag:
  • 常用类
  • 常用工具
  • web框架
  • UI库
  • icon
  • 跨平台
  • hooks
  • 状态类
  • 远程状态
  • 动画类
  • web 应用更新提示
  • web 服务器
  • 调试工具
  • api调试
  • 封装类
  • 数据类
  • 时间类
  • 布局类
  • 数值类
  • 富文本
  • 移动端
  • 鼠标/键盘相关
  • 文件处理
  • 图形/图像相关
  • Vue
    • vue组件
    • vue-form
  • Vuex
  • 动画
  • React
    • 常用
    • router
    • css
    • 动画
    • 图片相关
  • Node
    • web框架
    • orm
    • node版本管理
    • 更好的fs
    • 文件上传
    • 请求库
    • 爬虫
    • typescript
    • 插件
  • AI
    • 构建应用

Category: Tag:

common

  • web-music 一个简单的音乐播放器,播放周杰伦的歌曲
  • PreQuest http 请求库,支持 Web、小程序、ReactNative、快应用等
  • learn-regex Learn regex the easy way
  • editable 一款强到离谱的富文本编辑器框架
  • lunar 一个支持阳历、阴历、佛历和道历的日历工具库
  • journal-server 好享记账后端
  • Cent 完全免费、开源的多人协作记账
  • daily 基于 astro, golang, postgreSQL的日常收集优质内容
  • HuLa 基于Tauri、Vite 6、Vue 3 和 TypeScript 构建的即时通讯系统HuLa
  • TrendRadar

Category: Tag:
  • Tailwind CSS v4深度解析
  • Vue3+Nest全栈项目自动化部署:Github Actions + Docker
  • goer
  • gin
  • tanstack-query
  • react-router
  • shadcn
  • tanstack-query中文文档

Category: Tag:

2024

2025

  • 如何用 3-5 个高效问题快速识别 Golang 开发者的技术素养
  • 需要一个外国手机号,有持有成本低的吗
  • 使用 Claude Code 至今的一点经验分享
  • 移动端浏览器h5调试
  • 分享我用 cursor 的思路

Category: Tag:
  • 2022
  • 2023
  • 2024
  • 2025

2022

  • SpringBoot 同一个接口同时支持 form 表单、form-data、json 的优雅写法
  • 深入浅出音视频与 WebRTC 教育-成人与创新-前端团队(Elab团队)
  • 图片加载,咱们可以更酷炫一点
  • 性能优化之全面图片改造方案
  • 带你 封装ts axios,一次封装终身受益「美团后端连连点赞」
  • 还不会用 TS 封装 Axios?我教你啊
  • 如何优雅地中断 Promise
  • Promise 进阶实战与控制反转
  • 快图设计-基于fabric.js和Vue的开源图片编辑器
  • 将用户所见网页提取为图片
  • 2种方式!带你快速实现前端截图
  • 如何优雅地在 React 中使用TypeScript,看这一篇就够了
  • 12个 Typescript 开发实用小技巧清单
  • 精进 JavaScript | 这些手写你都会吗 常用的 js 原生方法实现
  • 原生拖拽太拉跨了,纯 JS 自己手写一个拖拽效果,纵享丝滑
  • Islands 架构原理和实践
  • 关于微前端架构的几种技术选型
  • Webpack落幕?其作者在Next.js 13中带来快700倍的替代品
  • Bun 是否能挑战 Webpack、Vite 的霸主地位
  • 微前端如何做样式隔离
  • React:我们即将和后端 API 告别
  • 前端:从零破解一款轻量级滑动验证码
  • 异步分片计算在腾讯文档的实践
  • Web 中的 选区 和 光标 需求实现
  • 不使用第三方库怎么实现【前端引导页】功能
  • 为什么说 90% 的前端不会调试 Ant Design 源码
  • 前端人必须掌握的抓包技能
  • 通过抓包,深入理解 DNS 流程和 CDN 原理
  • 自查,你的 React Hooks 够优雅吗
  • 如何将 NextJS 应用体验提升到极致
  • 从理解路由到实现一套Router(路由)
  • vue中动态引入图片为什么要是require
  • 微前端场景下的代码共享
  • 项目里没用过设计模式?看看 Nest.js 怎么用的
  • JavaScript Web 框架的 新浪潮
  • 前端不背锅,图片要这样压缩, 封装一个 tiny 命令行插件
  • 云谦:前端框架的趋势与实践(文字稿)
  • 云谦 CSS 方案 2022
  • CSS 2022 新特性
  • 从cdnjs 的漏洞来看前端的供应链攻击与防御
  • Vite项目屏幕适配的两种方案,超详细
  • 数据大屏最简单自适应方案,无需适配rem单位
  • React这25个精选库,将助你工作更上一层楼
  • dumi 2.0 发布啦!
  • CSS 实现自适应文本的头像
  • Web Audio API 太强了,让我们一起领略音频之美
  • 浅谈 React 组件设计
  • 超全面的前端工程化配置指南
  • 跨端开发浪潮中的变与不变
  • Rollup 与 Webpack 的 Tree-shaking
  • 基于 Graphql 的前后端协作方案
  • 微前端方案 qiankun 只是更完善的 single-spa
  • 纯 JS 实现语雀的划词高亮功能
  • 微信小程序web-view与H5 通信方案
  • 你只会用前端数据埋点 SDK 吗
  • 富文本如何搜索高亮?
  • 小心你复制的内容:使用零宽字符将用户名不可见的插入文本中
  • vue3+vant 开发微信公众号网页爬坑不完全指北
  • 这样封装列表 vue hooks,一天可以开发 20 个页面
  • 15 个 Vue3 全家桶开发的避坑经验
  • 为什么Proxy一定要配合Reflect使用
  • 使用前端技术实现静态图片局部流动效果
  • 基于 Webpack 项目接入 Vite 你可能需要注意的点
  • 图解Git分支和命令,这次懂了
  • 还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
  • 前端本地化部署
  • 使用Vue3封装一些有用的组合API
  • 面试官:请设计一个不能操作DOM和调接口的环境
  • B站的大数据平台建设之路
  • JWT 实现登录认证 + Token 自动续期方案,这才是正确的使用姿势
  • 关于无感刷新Token,我是这样子做的
  • 前端实现电子签名(web、移动端)通用组件
  • React 数据获取:避免条件竞争
  • 一文搞懂远程调试的基本原理
  • React 数据获取与性能优化
  • 包管理工具的演进
  • 浅谈前端包管理工具
  • 从实用角度浅析前端全链路质量监控中台技术方案
  • 如何从0到1搭建前端监控平台
  • 内网穿透你真的了解吗?
  • 从 React 源码彻底搞懂 Ref 的全部 api
  • 浅谈柯里化
  • vite项目为什么可以直接使用NODE_ENV

Category: Tag:

双因素认证

双重身份验证是一种验证用户身份的方法,它要求用户提供两项证明,例如在线帐户的密码(第一个因素)和身份验证应用程序的一次性密码(第二个因素)。

身份验证因素的类型

身份验证因素包括:

  • 知识因素
  • 持有因素
  • 固有因素
  • 行为因素

知识因素:用户知道的内容

知识因素是理论上只有用户才知道的信息。密码是最常见的知识因素。个人识别码 (PIN) 和安全问题的答案也很常见。

在大多数 2FA 实施方案中,知识因素是第一个身份验证因素。


Category: Tag:
  • Page Visibility API

  • Web Share API

  • Broadcast Channel API 通常用于在不同的标签页和窗口之间保持页面状态同步

  • Internationalization API

  • 前端如何使用 Access Token 和 Refresh Token

  • 使用 fetch 实现 sse


Category: Tag:

使用 fetch 手动解析 SSE 流

更灵活,适用于需要自定义请求方式,自定义请求头

普通版

export const createFetchSSE = (url) => {
  const decoder = new TextDecoder()
  const controller = new AbortController()
  fetch(url, {
    method: 'GET',
    signal: controller.signal,
    // headers: {},
  }).then((res) => {
    // 创建一个 ReadableStreamDefaultReader 去读取字节流数据
    const reader = res.body.getReader()
    const processHandle = ({ done, value }) => {
      if (done) {
        controller.abort()
        return
      }
      // value 为 Uint8Array 二进制数组
      const decodeValue = decoder.decode(value)
      const data = parseMessage(decodeValue)
      if (data.type === 'close') {
        controller.abort()
        return
      }
      // 读取下一个流数据
      return reader.read().then(processHandle)
    }
    reader.read().then(processHandle)
  })
}

function parseMessage(rawMessage) {
  const lines = rawMessage.split('\n')
  const message = {
    id: '',
    event: 'message',
    data: '',
    parsedData: null,
  }

  lines.forEach((line) => {
    const colonIndex = line.indexOf(':')
    if (colonIndex === -1) return
    const field = line.slice(0, colonIndex).trim()
    const value = line.slice(colonIndex + 1).trim()
    switch (field) {
      case 'data':
        // data 字段可以有多行,用 \n 连接
        message.data += (message.data ? '\n' : '') + value
        break
      case 'event':
        message.event = value
        break
      case 'id':
        message.id = value
        break
    }
  })
  // 尝试解析 JSON 数据
  if (message.data) {
    try {
      message.parsedData = JSON.parse(message.data)
    } catch (error) {
      message.parsedData = message.data
    }
  }
  return message
}

Category: Tag:

前端如何使用 Access Token 和 Refresh Token

核心理念

这个双令牌机制的核心是为了在 安全 和 用户体验 之间找到一个完美的平衡点:

  1. Access Token (访问令牌):

    • 用途:作为用户身份的凭证,附加在每个需要授权的 API 请求中。
    • 特点:生命周期短(例如 15 分钟到 4 小时)。即使被窃取,攻击者也只能在很短的时间内滥用它,从而降低了安全风险。
  2. Refresh Token (刷新令牌):

    • 用途:专门用来获取一个新的、有效的 Access Token。它本身不能用来访问 API。
    • 特点:生命周期长(例如 7 天到 30 天)。它使得用户在 Access Token 过期后,无需重新输入用户名和密码就能“无感”地刷新会话,保持登录状态,极大地提升了用户体验。

Category: Tag:

Reference

  • 前端环境变量配置:10个让你少加班的实战技巧


Category: Tag:

docker-compose.yml 示例

version: '3'

services:
  mysql:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    ports:
      - "3306:3306"
    volumes:
      - /opt/mysql:/var/lib/mysql

  redis:
    image: redis:latest
    ports:
      - "6379:6379"
    volumes:
      - /opt/redis:/data

  kafka:
    image: wurstmeister/kafka:latest
    depends_on: 
      - zookeeper
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9093,OUTSIDE://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
      KAFKA_LISTENERS: INSIDE://0.0.0.0:9093,OUTSIDE://0.0.0.0:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
  zookeeper:
    image: wurstmeister/zookeeper:latest
    ports:
      - "2181:2181"

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.15.0
    environment:
      - discovery.type=single-node
    ports:
      - "9200:9200"
  nacos:
    image: nacos/nacos-server
    environment:
      - MODE=standalone
    ports:
      - "8848:8848"
  system-biz:
    image: system-biz:1.0.1
    depends_on:
      - mysql
      - redis
      - nacos
      - elasticsearch
      - seaweedfs_master
      - kafka
    environment:
      - TZ=Asia/Shanghai
      - NACOS=-Dspring.cloud.nacos.config.server-addr=nacos:8848 -Dspring.cloud.nacos.discovery.server-addr=nacos:8848 -Dspring.cloud.nacos.config.password=nacos -Dspring.cloud.nacos.config.username=nacos -Dspring.cloud.nacos.discovery.username=nacos -Dspring.cloud.nacos.discovery.password=nacos
      - JAVA_OPTS=-Dserver.port=8080 -Xms1G -Xmx1G -Dspring.profiles.active=dev    -Duser.timezone=Asia/Shanghai
      - AGENT=-javaagent:skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=system-biz
    ports:
      - "10025:8080"
    volumes:
      - /opt/docker/system-biz:/app/logs

Category: Tag:

安装

linux

docker-compose

  1. 下载安装包
sudo curl -L https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-`uname -s`-`uname -m` -o usr/local/bin/docker-compose

Category: Tag:
  • https://podman.org.cn/
  • https://github.com/containers/podman
pip3 install podman-compose

前端下载文件遇到的一些问题

Category: Tag:
  • 前端下载文件遇到的一些问题
    • 前端下载文件的方式
      • 1、使用 a 标签下载
      • 2、使用虚拟 a 标签下载
      • 3、使用 window.open 方法下载
      • 4、使用 iframe 下载
      • 5、使用 blob + ObjectURL + a 标签的方式下载
    • 服务端通过修改 HTTP 协议头实现修改文件名
    • 参考链接
    • 相关文章

Category: Tag:

服务端下载

服务端返回文件并设置请求头 'Content-Disposition', 'attachment; filename="foo.png"'

客户端通过 a 标签直接链接到服务端接口地址即可

<a href="/api/foo">下载</a>

Category: Tag:
  • 原生 nodejs 下载
    • 预览
    • 下载

原生 nodejs 下载

const http = require('http')
const fs = require('fs')

http.createServer((req, res) => {
  // 设置下载文件名
  res.setHeader('Content-Disposition', 'attachment; filename="l.png"')
  // 设置响应头,告诉客户端这是一个文件下载
  // application/octet-stream 表示这是一个二进制流文件。浏览器遇到这种类型时,会将其视为二进制文件并触发下载行为
  // res.setHeader('Content-Type', 'application/octet-stream')
  // res.setHeader('Content-Type', 'image/png')
  // 方式一:读取文件直接返回
  fs.readFile('./logo.png', (err, data) => {
    if (err) {
      res.end('error')
    } else {
      res.end(data)
    }
  })
  // // 方式二:文件流式传输
  // const fileStream = fs.createReadStream('./logo.png')
  // // 将文件流管道到响应对象,实现自动传输
  // fileStream.pipe(res)
}).listen(4000, () => {
	console.log('running...')
})
rel=noopener 安全指引

Category: Tag:

target="_blank" 会在新窗口/标签页中打开链接。默认情况下,新窗口可以通过 window.opener 获取到原窗口的引用并进行操作(如重定向、注入脚本、窃取数据等)。为避免此类安全风险,应为外链添加 rel="noopener"。

为什么需要 noopener

  • 防止新页面通过 window.opener 操控原页面(tabnabbing 攻击)。
  • 提升安全性,避免潜在的钓鱼或恶意脚本注入。

Category: Tag:
  • ssh配置
  • 拉取
  • 配置
  • 暂存
  • 提交
  • 推送
  • 分支
  • 常见异常
    • CRLF/LF警告
      • 症状
      • 仓库级配置
      • 更稳妥的做法
      • 已有文件按规则重新规范化

Category: Tag:
  • nodejs
  • bash

nodejs

import child_process from 'child_process'

function getNow () {
  const d = new Date()
  const year = d.getFullYear()
  const month = d.getMonth() + 1
  const date = d.getDate()
  const hour = d.getHours()
  const minute = d.getMinutes()
  const second = d.getSeconds()
  return `${year}/${month}/${date} ${hour}:${minute}:${second}`
}
try {
  const [, , commitTitle] = process.argv
  const commands = [
    'git config user.name',
    'git config user.email',
    'git add .',
    `git commit -m "${commitTitle || 'mac2021 commit: ' + getNow()}"`,
    'git pull',
    'git push'
  ]
  child_process.execSync(commands.join(' && '), {
    stdio: 'inherit'
  })
} catch (error) {
  console.log('error: ', error)
}

Category: Tag:
  • GitHub520 定时获取 github 相关域名对应最优 ip 地址

获取到最新的 hosts 内容

  • hosts

Category: Tag:
  • 步骤一 :使用cd ~/.ssh切换工作目录,然后使用如下命令生成两个钥匙,中间一路回车。
  • 步骤二:创建配置文件 config
  • 步骤三:添加秘钥到ssh识别列表
  • 步骤四:添加 SSH key 及 测试
  • 步骤五:为了确认我们可以通过 SSH 连接 github,可通过输入下面命令来验证
  • 步骤六:用户名和邮箱配置
  • 步骤七:使用 git克隆仓库
  • 步骤八:远程地址添加或者修改
  • 相关文章

Category: Tag:

消息“远程:用户名或令牌无效。Git 操作不支持密码验证。” 表示您正在尝试使用用户名和密码对 Git 操作(例如git push、git pull或git clone)进行身份验证,但远程 Git 托管服务(例如 GitHub、Bitbucket)不再支持通过 HTTPS 对 Git 操作进行基于密码的身份验证。 这些服务要求使用个人访问令牌 (PAT) 或 SSH 密钥。 要解决此问题,您需要使用个人访问令牌或配置 SSH 密钥进行身份验证。 使用个人访问令牌(PAT): 生成个人访问令牌: 登录您的 Git 托管服务(例如,GitHub、Bitbucket)。 导航到您的帐户设置或开发者设置。 找到“个人访问令牌”或“应用程序密码”部分。 生成一个新令牌,提供一个描述性名称并选择 Git 操作所需的范围/权限(例如,repoGitHub 的范围)。 至关重要的是,立即复制生成的令牌, 因为它只会显示一次。 使用 PAT 进行身份验证: 当在 Git 操作期间提示输入密码时(例如git push),请输入您的 Git 用户名,然后粘贴个人访问令牌作为密码。 存储 PAT(可选,但建议使用): 为了避免重复输入令牌,可以配置 Git 的凭证助手: 代码


Category: Tag:
  • git pull
    • git fetch
    • git merge
  • git rebase
    • rebase命令使用
    • 自动变基
    • 自动变基的问题
  • 参考文章

Category: Tag:

安装依赖

# 安装单个
go get -u github.com/gin-gonic/gin
# 安装项目下中所有未被下载的依赖包
go mod download

Category: Tag:

questions

  • map 的值类型如何使用 string | int 这种联合类型 union type
  • map 类型如何复制
  • json 的序列化和反序列化
json 的序列化和反序列化

Category: Tag:

核心函数

  • 序列化:json.Marshal(v interface{}) ([]byte, error)
    将任意数据类型(通常是结构体、map、切片等)转换为 JSON 字节流。
  • 反序列化:json.Unmarshal(data []byte, v interface{}) error
    将 JSON 字节流解析到目标变量(需传入变量指针)。

1. 基本用法(结构体与 JSON 互转)

map 类型如何复制

Category: Tag:

在 Go 语言中,map 是引用类型(类似切片、通道),直接赋值只会复制引用(即两个变量指向同一个底层数据结构),修改其中一个会影响另一个。因此,若需要真正复制一个 map(即创建独立的副本,修改副本不影响原 map),需要手动遍历原 map 并将键值对逐一复制到新 map 中。

1. 错误的复制方式(仅复制引用)

直接赋值会导致两个 map 共享底层数据,修改任一都会相互影响:

package main

import "fmt"

func main() {
    original := map[string]int{"a": 1, "b": 2}
    copyMap := original // 仅复制引用,不是真正的复制

    copyMap["a"] = 100  // 修改副本
    fmt.Println(original["a"]) // 输出 100(原 map 也被修改了)
}
map 的值类型如何使用 string | int 这种联合类型 union type

Category: Tag:

在 Go 语言中,map 的值类型不能直接使用 string | int 这种联合类型(union type),因为 Go 是一门强类型语言,且在 Go 1.18 引入泛型之前,并不支持联合类型;即便有了泛型,map 的值类型也必须是一个确定的类型(或通过泛型参数约束的类型集合)。

具体原因:

1. Go 不支持原生联合类型

Go 语法中没有 string | int 这样的联合类型表示方式(不同于 TypeScript 等语言)。变量、函数返回值、集合元素的类型必须是单一确定的类型(如 string、int、自定义结构体等),或通过泛型参数定义的 “类型集合”。


Category: Tag:
  • 查日志
    • 基本
    • vi
    • 组合拳
      • 场景一:查异常堆栈,绝不能只看一行!
      • 场景二:实时看新日志
      • 场景三:翻历史日志 & 压缩日志
      • 场景四:统计异常数量
    • 常用 grep 参数
  • Reference

Category: Tag:

系统信息

uname -a # 显示操作系统名称、主机名、内核版本、硬件架构等信息
lsb_release -a # 提供Linux 发行版的详细信息,包括名称、版本号和代号

Category: Tag:
  • 一、前置条件
  • 二、基础连接:密码登录
    • 步骤 1:打开本地终端(或 SSH 工具)
    • 步骤 2:执行 SSH 连接命令
    • 步骤 3:确认连接并输入密码
  • 三、进阶:密钥登录(更安全)
    • 步骤 1:本地生成密钥对
    • 步骤 2:将公钥上传到远程服务器
      • 方式 1:使用 ssh-copy-id 自动上传(推荐)
      • 方式 2:手动上传(无 ssh-copy-id 时)
    • 步骤 3:测试密钥登录
  • 四、配置别名
    • ~/.ssh/config文件
    • 配置步骤
  • 五、常用 SSH 命令选项
  • 六、常见问题与解决*
  • 六、安全建议

Category: Tag:
  • 模式
  • 光标移动
  • 文本编辑
  • 撤销
  • 文件保存与退出
  • 翻页
  • 查找
  • 替换
  • 其它底线命令
  • 删除
    • 删除全部内容
    • 删除包含模式的行
  • 复制
    • 全选复制
  • 缩进(格式化)
  • 补全

Category: Tag:

强制关闭某个端口

  1. 打开 cmd 命令窗口,根据端口号查询这个端口号的 PID
netstat -ano|findstr "9000"

Category: Tag:
  • 最佳代理实践
    • 购买机场
    • 安装 v2raya
    • 添加订阅
    • 配置 v2raya
    • 备注
    • 规则配置
    • 手机端
  • Reference

Category: Tag:

在最佳代理实践中, 代理主要有两种:

  1. 一种主要是 Trojain, 配合 Clash 实现自动代理, 主要服务于浏览器上网查资料
  2. 第二种通过 sshuttle 来实现全局代理, 主要是保障命令行的工具一定可以工作, 比如 git、 goget、 yay 等场景

sshuttle 这个工具非常稳定, 各种恶劣的环境都可以保证命令行工具正常运行, 但是它有两个缺点:

  1. 全局的特性会干扰浏览器的正常运行, 会让一些不用代理的网站访问速度变慢, 或者让微信无法接受新消息
  2. 每次都需要开一个终端输入密码, 很麻烦 其实, 我平常用的最多的命令就是 Git, 访问 git https 一般都没有问题, 主要是 git ssh 容易被干扰, 原因是防火墙有时会完全拒绝 SSH 链接。

Category: Tag:
  • 症状
  • 可能的原因和解决方法
    • 机场模式选择不对,可以试试 tun 模式
    • 机场服务商把 ssh 协议 or 22 端口封了,需要设置下 git via https
    • git config proxy
    • 命令行 proxy
    • 仓库目录下执行 git config --local -e ,弹出文件编辑,git@github.com:改为 https://github.com/
  • Reference

Category: Tag:
  • fnm
    • 手动安装
    • 配置 fnm
      • git bash
      • powershell
      • cmd
    • 参考

Category: Tag:

mysql-pool

const mysql = require('mysql')

// Create connection pool (recommended approach)
const pool = mysql.createPool({
  connectionLimit: 10, // Limit the number of connections
  host: 'localhost',
  user: 'root',
  password: 'In123456.',
  database: 'r3',
  acquireTimeout: 60000,
  timeout: 60000,
  reconnect: true
})

// Test the pool connection
function testConnection() {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        console.log('Error getting connection from pool:', err.stack)
        reject(err)
      } else {
        console.log('Connected to MySQL pool as id ' + connection.threadId)
        connection.release() // Always release the connection back to pool
        resolve()
      }
    })
  })
}

// Execute query using connection pool
function exec(sql, data = []) {
  return new Promise((resolve, reject) => {
    pool.query(sql, data, (error, results) => {
      if (error) {
        console.log('Exec error:', error)
        return reject(error)
      }
      resolve(results)
    })
  })
}

// Get a connection from pool (for transactions)
function getConnection() {
  return new Promise((resolve, reject) => {
    pool.getConnection((err, connection) => {
      if (err) {
        reject(err)
      } else {
        resolve(connection)
      }
    })
  })
}

// Close the pool (call this when shutting down the app)
function closePool() {
  return new Promise((resolve) => {
    pool.end(() => {
      console.log('MySQL pool closed')
      resolve()
    })
  })
}

module.exports = {
  pool,
  testConnection,
  exec,
  getConnection,
  closePool
}

Category: Tag:
npm config set registry https://registry.npmjs.org

npm config set registry https://registry.npmmirror.com

Category: Tag:

自动生成excel文件

import path from 'path'
import { fileURLToPath } from 'url'
import { dirname } from 'path'
import ExcelJS from 'exceljs'
import Mock from 'mockjs'

// 准备模板文件(template.xlsx),表头示例

// 姓名({{name}})年龄({{age}})邮箱({{email}})加入时间({{joinDate}})

function generateRandomPhoneNumber() {
  // 随机生成第二个数字(号段)
  const phoneSegments = ['3', '5', '7', '8']; // 可选的第二位数字段
  const randomSegment = phoneSegments[Math.floor(Math.random() * phoneSegments.length)];

  // 生成后9位数字
  let remainingNumber = '';
  for (let i = 0; i < 9; i++) {
    remainingNumber += Math.floor(Math.random() * 10);
  }

  // 拼接成完整的手机号码
  return '1' + randomSegment + remainingNumber;
}

const __dirname = dirname(fileURLToPath(import.meta.url))

// 配置路径
const templatePath = path.resolve(__dirname, 'batch-import.xlsx');
const outputPath = path.resolve(__dirname, 'batch-output100.xlsx');

// 模拟批量数据
// https://github.com/nuysoft/Mock/wiki/Getting-Started
const mockData = []

for (let i = 0; i < 100; i++) {
  mockData.push({
    name: Mock.Random.cname(),
    age: Mock.Random.integer(18, 28),
    mobile: generateRandomPhoneNumber(),
    email: Mock.Random.string('number', 10, 10 ) + '@qq.com',
    joinDate: Mock.Random.date('yyyy-MM-dd'),
    remark: 'test' + (i + 1)
  })
}

async function main() {
  try {
    // 1. 读取模板
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.readFile(templatePath);
    const worksheet = workbook.getWorksheet(1);

    // 2. 设置表头
    const headerRow = worksheet.getRow(1);
    // 替换表头(默认生成的会有变量占位符)
    headerRow.values = ['姓名', '年龄', '手机', '邮箱', '加入时间', '备注'];
    // // 设置表头样式
    // headerRow.font = { name: '微软雅黑', size: 12, bold: true };
    // headerRow.fill = {
    //   type: 'pattern',
    //   pattern: 'solid',
    //   fgColor: { argb: 'FFE0E0E0' },
    // };

    // 3. 填充数据(行级填充)
    const startRow = 2; // 假设第一行是表头,从第二行开始填充
    mockData.forEach((item, index) => {
      const row = worksheet.getRow(startRow + index);
      row.values = [
        item.name,
        item.age,
        item.mobile,
        item.email,
        item.joinDate,
        item.remark,
      ];
    });
    // // 追加(如果有空行不会替换)
    // mockData.forEach(item => {
    //   worksheet.addRow([item.name, item.age, item.email, item.joinDate]);
    // });

    // 4. 保存文件
    await workbook.xlsx.writeFile(outputPath);
    console.log(`成功生成文件:${outputPath}`);
  } catch (error) {
    console.error('生成失败:', error.message);
  }
}

main();

Category: Tag:
  • 1:安装
  • 2:注册 acme.sh 账户
  • 3:切换 CA 服务器为 Let's Encrypt
  • 4:配置 Aliyun API 凭据
  • 5:生成证书
  • 6:安装证书到 Nginx
    • 1. 添加 SSL 配置
    • 2. 强制 HTTPS 重定向
  • 7:自动更新证书
  • 8:查看已安装证书信息
  • Reference

Category: Tag:
  • 前端部署
    • 常规做法
      • 打包文件名 hash 化
      • HTML 入口文件防缓存
      • 被忽略的缓存策略
    • 精细化 Nginx 缓存策略
    • CDN设置
  • Reference

Category: Tag:

我来分析一下日志文件,并给出优化建议。

基于对日志的分析,我发现了以下安全隐患和异常情况:

📊 日志分析结果

🚨 严重安全问题

  1. 恶意 PHP 文件扫描攻击(大量)

    • 攻击者尝试访问大量 WordPress 相关的 PHP 文件

    • 如:/wp-admin/, /wp-content/, /wp-includes/ 等

    • 来源 IP: 4.194.58.148, 4.197.60.90

  2. 路由器漏洞扫描

    • /cgi-bin/luci/;stok=/locale - TP-Link 路由器漏洞

    • 来源 IP: 87.120.191.100, 91.224.92.109

  3. Git 配置文件探测

    • /.git/config - 尝试获取源码

    • 已被规则拦截(403)

  4. 文件上传漏洞扫描

    • /upload.php, /uploadfile.php 等
  5. SQL 注入尝试

    • DedeCMS 相关的复杂注入攻击参数

Category: Tag:

你想了解Nginx中deny all;和return 403的核心区别,以及它们是否会被记录到access.log中,我来为你详细拆解:

一、核心区别(从本质到使用场景)

1. 所属模块与本质不同

  • deny all;:属于ngx_http_access_module(访问控制模块),本质是访问权限控制指令,作用是基于客户端IP(或IP段)直接拒绝访问,是Nginx层面的"访问黑名单"机制,专门用于IP级别的访问限制。
  • return 403;:属于ngx_http_rewrite_module(重写模块),本质是响应返回指令,作用是主动向客户端返回指定的HTTP状态码(此处为403 Forbidden),不依赖IP,是通用的响应控制手段。

Category: Tag:

日志分析

1. 主要异常情况

从 error.log 中可以看到以下几类异常:

a) 恶意扫描和攻击尝试

  • WordPress 相关扫描:大量针对 WordPress 文件的扫描(wp-login.php, wp-admin/, wp-content/ 等)

  • PHP 后门扫描:尝试访问各种 PHP 后门文件(alfa.php, admin.php, shell.php 等)

  • 路径遍历攻击:尝试访问 .git/config, .env 等敏感文件

  • CGI 漏洞扫描:/cgi-bin/luci/;stok=/locale

  • 文件上传漏洞扫描:upload.php, uploadfile.php 等


Category: Tag:
  • 1. 安装
    • curl方式
    • git方式
  • 使用
  • 续订证书
  • 常见问题
  • Reference

Category: Tag:
  • 安装
    • yum 安装
    • 源码包 安装
    • yum 和 源码包 安装的区别
      • 安装路径不同
      • 启动方式不同
    • Reference

Category: Tag:

你想了解Nginx中limit_conn和limit_req的核心区别及实际使用方法,我会从本质差异、工作原理、使用场景和配置示例等方面为你详细解析:

一、核心区别(核心定位不同)

limit_conn和limit_req均是Nginx的流量限制模块,但管控的对象和维度完全不同,这是二者的核心差异:

特性 limit_conn limit_req
核心定位 限制并发连接数(同一客户端的同时连接数量) 限制请求速率(单位时间内的请求次数,基于漏桶/令牌桶算法)
管控对象 客户端(通常按IP、用户标识分组)的连接数 客户端的请求数(单个连接内可包含多个请求,如HTTP长连接中的多次请求)
解决问题 防止单个客户端同时建立大量连接耗尽服务器资源(如并发连接攻击) 防止单个客户端在短时间内发送大量请求(如CC攻击、高频接口调用)
依赖模块 ngx_http_limit_conn_module ngx_http_limit_req_module
算法基础 基于连接计数(简单统计当前活跃连接数) 基于漏桶算法(默认)/令牌桶算法(burst+nodelay实现)

Category: Tag:
  • deny all 和 return 403 有什么区别,它们都会记录在 access.log 中吗
  • limit_conn 和 limit_req 的使用

Category: Tag:

Reference

  • 如何让 Nginx 更安全?

Category: Tag:

iSH 是免费开源的软件,能满足 iOS 下基本终端使用需求,如 git 拉取项目、通过 ssh 管理服务器等。

iSH

创建 ssh 秘钥

无法通过 rsa 创建秘钥,支持部分模式,如 ed255191 :

ssh-keygen -t ed255191

Category: Tag:
  • 安装包
  • 同步步骤
    • 创建git仓库
    • PC 同步
    • ios 同步
    • Android 同步
  • iOS 上 iSH 执行 git 命令卡住的问题
  • 相关

Category: Tag:
  • 概念篇
    • React 的核心思想
    • 渲染器
    • reconcilers
    • Stack reconciler
    • Fiber reconciler
    • Fiber 架构
      • 什么是 fiber?
      • 两个阶段的拆分
        • Reconciliation 阶段
        • Commit 阶段
    • 类组件
      • 生命周期
      • setState
      • forceUpdate
      • static defaultProps
      • static displayName
    • 函数式组件
    • 事件处理
      • 合成事件
      • 事件触发顺序
      • 事件处理函数
      • 向事件处理程序传递参数
    • key
    • ref
      • 何时使用 ref
      • Refs 与函数组件
      • 将 DOM Refs 暴露给父组件
    • context
      • Context.Constomer 和 函数式组件
      • 动态 context
      • 在嵌套组件中更新 Context
      • 消费多个 Context
      • 意外渲染
    • 高阶组件
      • 不要改变原始组件
      • 将不相关的 props 传递给被包裹的组件
      • 最大化可组合性
      • 包装显示名称以便轻松调试
      • 不要在 render 方法中使用 HOC
      • 务必复制静态方法
      • Refs 不会被传递
    • render props
      • 对比高阶组件
      • render prop 优化

Category: Tag:
  • createRef vs useRef
  • FC每次渲染获取到的状态不是实时状态?

在class时代,由于组件节点是通过class实例化而得,因此可以在类实例上存放内容,这些内容随着实例化产生,随着componentWillUnmount销毁。但是在hook的范围下,函数组件并没有this和对应的实例,因此useRef作为这一能力的弥补,扮演着跨多次渲染存放内容的角色。


Category: Tag:
  • setState 是同步还是异步相关问题
    • setState 是同步还是异步?
    • 何时是同步,何时是异步呢?
    • 那为什么会出现异步的情况呢?
    • 那如何在表现出异步的函数里可以准确拿到更新后的 state 呢?
    • 那表现出异步的原理是怎么样的呢?

Category: Tag:
  1. JavaScript 基础深度对闭包、事件循环(Event Loop)、原型链等核心概念理解模糊
  2. 框架原理理解停留在 API 使用层面,对虚拟 DOM Diff 算法、Hooks 依赖收集原理、响应式原理等理解不深
  3. 项目经验表述多停留在"用了什么技术",缺乏对技术选型原因、遇到的复杂问题及解决方案、量化成果的深入阐述
  4. 工程化能力 对构建工具(如 Webpack、Vite)、CI/CD、自动化测试、代码规范等接触较少或理解不深
  5. 浏览器与网络对浏览器渲染机制、性能优化(如减少重绘回流、懒加载、预加载等)、HTTP/HTTPS 协议、缓存策略等理解不够全面
  6. 计算机基础知识对数据结构、算法、操作系统、计算机网络等基础概念

Category: Tag:
  • Vue 概念篇
    • 响应式原理
    • 检测变化的注意事项
    • 异步更新队列
    • 计算属性vs侦听属性
    • 指令
      • v-if 和 v-show
      • v-for 和 v-if 优先级
    • 事件修饰符
    • 在组件上使用 v-model
    • .sync 修饰符
    • 缓存组件 keep-alive
    • 动态组件 component
    • 异步组件
  • 开发实战篇
    • 跨域问题
      • CORS
      • 代理
    • 路由参数解耦
    • 组件的自动化全局注册
    • 绑定 key 值
    • 样式穿透(深度作用选择器)
    • 程序化的事件侦听器
    • 命令式调用组件
  • 代码层面优化
  • Vue 可用的渲染优化
  • 构建优化
  • 通用优化
Vue 生命周期

Category: Tag:
  • Vue 生命周期
    • 父子组件
      • 总结
    • 兄弟组件
      • 总结
    • mixin
      • 总结

Category: Tag:
  • 两种设置 props 默认值的方法
    • props 解构赋值默认值
    • withDefaults
  • (Array<Foo | Bar>) and (Foo[] | Bar[])
  • ...rest 剩余参数或者 arguments 参数传递的类型错误

Category: Tag:

mongodb


Category: Tag:
  • 表结构
  • 角色
    • 创建
    • 修改
    • 分页查询
    • 删除

Category: Tag:

SQL : Execution Order 在写 SQL 时,我们习惯从 SELECT 开始,但数据库在解析和执行时,其实遵循一套固定的逻辑顺序。理解这个执行流程,对调优、避免语法误解都非常重要。 🔹 标准 SQL 的执行顺序如下: 1️⃣ FROM / JOIN • 确定数据源,完成表的连接 • 这里决定了后续操作的基础数据集 2️⃣ WHERE • 对行进行过滤 • 注意此阶段还不能使用聚合函数和别名 3️⃣ GROUP BY • 将数据按指定字段分组 • 每组成为一个聚合的基本单元 4️⃣ HAVING • 对分组结果进行过滤 • 常与聚合函数配合使用,如 HAVING COUNT(*) > 1 5️⃣ SELECT • 选择需要输出的列 • 计算表达式,执行聚合函数 • 在此阶段别名才会生效 6️⃣ ORDER BY • 对结果集排序 • 可以使用 SELECT 中定义的别名 ⚠️ 常见误区: • WHERE 不能用聚合函数,因为聚合在 GROUP BY 之后才发生 • HAVING 主要作用于分组,而不是单行 • ORDER BY 才是最终展示顺序,和存储顺序无关


Category: Tag:
  • 安装
    • 安装 MySQL rpm 源信息
    • yum安装
    • 验证 MySQL 安装
    • mac 下安装
    • 设置密码
    • 常用命令
    • 查看MySQL 配置文件
    • 导出 sql 文件
  • Reference

Category: Tag:
  • 一、核心区别总结
  • 二、语法详解
    • 1. 内连接(INNER JOIN)
    • 2. 左外连接(LEFT JOIN)
    • 3. 右外连接(RIGHT JOIN)
    • 4. 全外连接(FULL OUTER JOIN,MySQL 不直接支持)
  • 三、示例说明(附表结构)
    • 示例 1:内连接(INNER JOIN)
    • 示例 2:左外连接(LEFT JOIN)
    • 示例 3:右外连接(RIGHT JOIN)
    • 示例 4:全外连接(FULL OUTER JOIN,模拟)
  • 四、关键注意事项
  • 总结
select * from table where id in (上千id) 超时了,in的奇特优化方法

Category: Tag:

有时候sql的优化不一定非得依靠数据库去解决,毕竟传统的数据库是用来管理数据的,性能是有限的,如果能从程序上优化的话是最好的选择。

关于这条sql的优化方法有很多种,下面是简单的几种

方法一:采用多线程,把id分成多段去执行,然后再组装。

select * from table where id in (1-100); select * from table where id in (101-200); 以此类推... ...

方法二:完全采用数据库,使用临时表

-- 1. 创建临时表 create temporary table tmp_ids (id BIGINT PRIMARY KEY);


Category: Tag:
  • 在 MySQL 中处理 role 表与 resource 表的多对多关系查询方案
    • 方案 1:直接 JOIN 关联(推荐)
    • 方案 2:先查角色 IDs,再用 IN 关联(特定场景适用)
    • 方案 3:子查询关联(避免应用层多次交互)
    • 性能优化建议
    • 总结

Category: Tag:
  • 1、使用explain命令查看sql的执行计划,它能够简单分析sql的执行情况,是否走索引等。
  • 2、in和not in慎用
  • 3、不用SELECT * ,只查询需要的字段。
  • 4、limit的使用
  • 5、order by字段建索引
  • 6、count用法
  • 7、where 子句中避免is null /is not null
  • 8、尽量避免在 where!=或<>
  • 9、尽量避免在 where 子句中使用 or
  • 10、尽量避免在where子句中对字段进行函数操作
  • 11、尽量避免like '%xxx'式查询
  • 12、选择重复值较低的字段建索引
  • 13、注意索引顺序
  • Reference

Category: Tag:

中间表

多对多关系的中间表通常用于连接两个主表(如 role和 resource),并记录两者之间的关联关系。

中间表的字段设计需注意:

  • 主键:无需添加无需自增主键 id(避免冗余存储,且浪费空间(每条记录多存一个 INT)),而使用复合主键((userId, roleId)),确保唯一性;

  • 外键:外键字段应非空,且需为 userId 和 roleId 分别添加外键约束,关联到 userId.id 和 role.id;

  • 索引:

    • userId作为联合主键的第一列,主键索引((userId, roleId))已经能够高效支持基于 userId的查询,无需额外创建索引。
    • InnoDB 外键约束依赖索引来保证性能,若外键列未显式创建索引,InnoDB 会自动创建一个(但命名可能不友好)。而手动创建的索引可以使用有意义的名称(如 idx_role_id),方便后续维护。
    • 为 roleId单独创建 idx_role_id索引,若已建表可后面再补充(CREATE INDEX idx_role_id ON user_role(roleId);)
      • roleId不是主键的一部分:主键索引的排序是 (userId, roleId),因此 roleId的值在主键索引中是无序的(仅作为主键的第二列)。
      • 反向查询需要高效索引:如果业务中需要频繁通过 roleId查询关联的用户(例如查询角色 X 被哪些用户拥有),即 WHERE roleId = Y,此时主键索引无法直接利用(因为主键的第一列是 userId,不是 roleId),必须通过 roleId的单独索引快速定位。
  • 时间字段的类型选择

    • 当前选择:TIMESTAMP类型,占用 4 字节,时间范围为 1970-01-01 00:00:01到 2038-01-19 03:14:07。
    • 可选方案:若业务需要更早或更晚的时间(如记录历史数据),可改用 DATETIME类型(占用 8 字节,时间范围 1000-01-01 00:00:00到 9999-12-31 23:59:59)。
  • 软删除字段的查询过滤

    • 问题:isDeleted字段用于逻辑删除,但需确保应用层在查询时主动过滤已删除的记录(如 WHERE isDeleted = 0)。若忘记过滤,可能导致查询到无效数据。
    • 建议:通过视图或触发器自动过滤,或在应用层统一封装查询逻辑(推荐)。
  • 可选字段:若需记录额外信息(如创建人),可添加 createByName、createById 等字段,但需避免过度设计。

    • ALTER TABLE user_role ADD createByName varchar(100) AFTER createdAt

Category: Tag:
  • Problem: Access denied for user 'root'@'localhost' (using password: YES)
    • Option 1: Using MySQL Installer (Recommended for Windows)
    • Option 2: Safe Mode Reset
    • Option 3

Category: Tag:

postgresql


Category: Tag:
  • 一、缓存穿透(Cache Penetration)
    • 定义
    • 核心特征
    • 实际场景
    • 示例
  • 二、缓存雪崩(Cache Avalanche)
    • 定义
    • 核心特征
    • 实际场景
    • 示例
  • 三、缓存击穿(Cache Breakdown)
    • 定义
    • 核心特征
    • 实际场景
    • 示例
  • 四、三者区别对比
  • 五、解决方案
    • 缓存穿透
    • 缓存雪崩
    • 缓存击穿
  • 总结
  • Reference

Category: Tag:

redis

🚀 部署检查清单

Category: Tag:
  • 🚀 部署检查清单
    • 部署前准备
      • ✅ 代码准备
      • ✅ 服务器环境
      • ✅ GitHub 配置
      • ✅ 数据库准备
    • 部署过程
      • 🔄 自动部署 (GitHub Actions)
      • 🔧 手动部署 (备用方案)
    • 部署后验证
      • ✅ 服务状态检查
      • ✅ 功能测试
      • ✅ 性能检查
      • ✅ 安全检查
    • 常见问题排除
      • 🔧 容器启动失败
      • 🔧 网络连接问题
      • 🔧 数据库连接失败
      • 🔧 权限问题
    • 回滚方案
      • 🔄 代码回滚
      • 🔄 容器回滚
      • 🔄 数据库回滚
    • 监控和维护
      • 📊 日常监控
      • 🧹 定期维护
      • 📈 性能优化
Docker多项目部署指南

Category: Tag:
  • Docker多项目部署指南
    • 部署步骤
      • 1. 安装Docker和Docker Compose
      • 2. 构建和启动容器
      • 3. 访问应用
    • 故障排除
      • /r3/api/ 路径请求失败问题排查
      • 修改 服务名 和 容器名 后启动报端口冲突异常
      • connect() failed (111: Connection refused) while connecting to upstream,

Category: Tag:
  • SSH密钥配置
  • 故障排除
    • GitHub Actions 构建异常,日志错误信息 Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)
    • /r3/api/health get请求正常,/r3/api/auth/login post 请求 404
    • nginx 日志时间, r3-admin-server 服务中的时间不对

Category: Tag:
  • 项目结构
    • 方案概述
    • 详细步骤
      • 1. 创建个人访问令牌 (PAT)
      • 2. 准备中央部署仓库
      • 3. 在每个服务仓库中配置触发工作流
    • 总结
  • 环境变量配置
    • 方案一:在服务器上使用 .env 文件,推荐使用
      • 1. 登录到生产服务器
      • 2. 创建 .env 文件
      • 3. 编辑并填充环境变量
      • 工作原理
    • 方案二:使用 GitHub Secrets (不推荐)
    • 总结

Category: Tag:

您好,这是一个很好的问题,涉及到 gin 框架中数据校验的细节。

validate:"required" 和 binding:"required" 都用于确保字段在请求中存在且不为空,但它们来自不同的校验库,并且在 gin 中的使用场景略有不同。

  • binding:"required":这是 gin 框架内置的校验功能。当您使用 c.ShouldBindJSON()、c.ShouldBindXML() 或其他 ShouldBind 系列方法时,gin 会自动检查这些 binding 标签。它简单、直接,足以满足大多数非空校验的需求。

  • validate:"required":这通常与一个第三方的、功能更强大的校验库(如 go-playground/validator)配合使用。gin 可以与这类库集成,以支持更复杂的校验规则,例如:

    • validate:"min=6,max=20" (字符串长度)
    • validate:"email" (邮箱格式)
    • validate:"url" (URL格式)
    • validate:"oneof=male female" (枚举值)

Category: Tag:

获取参数

获取 路径 参数

idStr := c.Param("id")
id, err := strconv.ParseUint(idStr, 10, 64)

Category: Tag:

question

  • validate:"required" 和 binding:"required"的区别
  • c.ShouldBindJSON() 和 c.ShouldBind() 的区别

Category: Tag:

这是一个关于 gin 框架数据绑定的核心问题。c.ShouldBindJSON() 和 c.ShouldBind() 的主要区别在于特异性和灵活性。


c.ShouldBindJSON()

这个方法专门用于将请求体中的 JSON 数据绑定到您指定的 struct 上。

  • 强制性:它强制要求请求的 Content-Type 头部必须是 application/json。如果不是,绑定会立即失败。
  • 目标明确:它的意图非常清晰,只处理 JSON 数据。这使得代码更具可读性,因为您一眼就能看出这个处理器只接受 JSON 格式的输入。
Docker多项目部署指南

Category: Tag:

本文档提供了使用Docker部署多个项目的详细说明,包括qyhever博客、r3-admin-front前端和r3-admin-server后端服务。

项目结构

  • nginx: 主 nginx,统一处理所有路由和 API 代理,访问路径为根目录 http://localhost/
  • qyhever: 博客项目,访问路径为 http://localhost/blog/
  • r3-admin-front: 管理前端项目,访问路径为 http://localhost/r3-admin/
  • r3-admin-server: 管理后端API服务,为r3-admin-front提供API支持