nodejs基础

安装

nodejs安装

# installs nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
# download and install Node.js (you may need to restart the terminal)
nvm install 20
# verifies the right Node.js version is in the environment
node -v # should print `v20.18.0`
# verifies the right npm version is in the environment
npm -v # should print `10.8.2`

# 安装pnpm
npm install -g pnpm

# 在容器中
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash && \
  export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" &&  [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" && \
  nvm install 20 && npm install -g pnpm
# 容器中运行指令
RUN /bin/bash -c "[ -s /root/.nvm/nvm.sh ] && . /root/.nvm/nvm.sh && cd /app && pnpm install --registry=https://registry.npmmirror.com"
RUN /bin/bash -c "[ -s /root/.nvm/nvm.sh ] && . /root/.nvm/nvm.sh && cd /app && npx prisma generate"

常用包安装

# 安装pnpm
npm install -g pnpm

# 创建一个新的next.js项目
pnpm create next-app@latest my-nextjs-app

# 安装nextui
pnpm add @nextui-org/react

# 安装prisma
pnpm add @prisma/client

# 安装nextauth
pnpm add next-auth
pnpm add @next-auth/prisma-adapter
pnpm add bcrypt

框架推荐

后端微服务

  • nestjs:微服务框架,用于实现API服务器或者WS接口服务器
  • swagger:接口文档工具,同时支持测试接口
  • sequelize-typescript:一个需要手写数据库定义的orm
  • @kubernetes/client-node:k8s client库,用于调度和查询k8s资源
  • axios:用于发送http请求

全栈

  • Next.js (前后端统一的React框架)
  • nextui React的UI库
  • Tailwind CSS:方便的布局和style定义方式,clsx
  • prisma: 一个好用的ORM

npm国内源

临时使用pnpm install --registry=https://registry.npmmirror.com 全局修改pnpm config set registry https://registry.npmmirror.com或者修改~/.npmrc添加registry=https://registry.npmmirror.com

常见问题

pnpm run build

遇到collect page data error

在collect page data的时候,会实际调用执行过程,如果遇到error的话会报错,举个例子

import OSS from 'ali-oss';

class AliOssClient {
  private region: string;
  private accessKeyId: string = "";
  private accessKeySecret: string = "";
  private bucket: string;
  // private baseUrl: string;
  private endpoint: string;
  private internalEndpoint: string;
  private client: OSS;

  constructor(isInternal: boolean) {
    // 从环境变量中加载配置
    this.region = process.env.ALI_OSS_REGION || 'oss-cn-hangzhou';
    this.accessKeyId = process.env.ALI_OSS_KEY || '';
    this.accessKeySecret = process.env.ALI_OSS_SECRET || '';
    this.bucket = process.env.ALI_OSS_BUCKET || '';
    // this.baseUrl = process.env.ALI_OSS_BASE_URL || '';
    this.endpoint = process.env.ALI_OSS_ENDPOINT || '';
    this.internalEndpoint = process.env.ALI_OSS_INTERNAL_ENDPOINT || 'default';

    // 初始化 OSS 客户端
    this.client = new OSS({
      region: this.region,
      accessKeyId: this.accessKeyId,
      accessKeySecret: this.accessKeySecret,
      bucket: this.bucket,
      endpoint: isInternal ? this.internalEndpoint : this.endpoint,
    });
  }

  /**
   * 获取签名 URL
   * @param objectKey 文件对象的路径
   * @param expires 签名有效时间(秒)
   * @returns 签名 URL
   */
  async getSignedUrl(objectKey: string, expires: number): Promise<string | null> {
    try {
      // 拼接完整路径
      const url = this.client.signatureUrl(objectKey, { expires });
      return url;
    } catch (err) {
      console.error('Error generating signed URL:', err);
      return null;
    }[]()
  }
}

export const aliOSSClient = new AliOssClient(false);
export const aliOSSInternalClient = new AliOssClient(true);

调用这个文件的路由,在build的时候就会出现collect page data error,因为初始化调用过程中.env中并没有映射数据,所以是空的,导致new OSS的输入参数部分为空,但是OSS的初始化函数会检测是否为空。最直接的解决办法就是,将这些变量的空值改成'default'

pnpm approve-builds

pnpm默认不会自动编译任何库,需要交互式的方式执行pnpm approve-builds,这在Dockerfile中不友好,需要提前在package.json中配置pnpm.onlyBuiltDependencies举个例子:

...
	"pnpm": {
		"onlyBuiltDependencies": [
			"@prisma/client",
			"@prisma/engines",
			"bcrypt",
			"cpu-features",
			"prisma",
			"sharp",
			"ssh2",
			"tos-crc64-js"
		]
	},
...

MFA二次认证

首先需要安装两个库speakeasyqrcode

pnpm add speakeasy qrcode --registry=https://registry.npmmirror.com

创建mfaSecret:

import speakeasy from 'speakeasy';

// 生成 MFA Secret
const secret = speakeasy.generateSecret({
  length: 20, // Secret 的长度(默认 32 字节)
  name: 'MyApp', // 应用名称(显示在验证器应用中)
  issuer: 'MyApp', // 发行者名称(显示在验证器应用中)
});

console.log('MFA Secret:', secret.base32); // Base32 编码的 Secret
console.log('OTPAuth URL:', secret.otpauth_url); // OTPAuth URL,用于生成二维码

生成二维码图片:

import QRCode from 'qrcode';

// 生成二维码图片
QRCode.toDataURL(secret.otpauth_url, (err, data_url) => {
  if (err) {
    console.error('Error generating QR code:', err);
    return;
  }
  console.log('QR Code Data URL:', data_url); // 二维码的 Data URL
});
// 网页中显示
<img src="<%= data_url %>" alt="MFA QR Code">

验证一次性密码:

const verified = speakeasy.totp.verify({
	secret: mfaSecret, // 用户的 MFA Secret
	encoding: 'base32', // Secret 的编码格式
	token: userInputToken, // 用户输入的一次性密码
});