开发 todos 网站的栽坑记录

docker nginx 静态页面

使用 nextjs,先做的静态页面,用的最简单版腾讯云,启动docker ngnix 静态网页部署,过程是直接上传本地打包文件直接到服务器,

docker run  -d  -p 80:80  
-v /home/docker-nginx/nginx.conf:/etc/nginx/nginx.conf  
-v /home/docker-nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
-v /home/lighthouse/out:/usr/share/nginx/html  
--name mynginx

一一解释, -p 80:80 本地docker 80 外面监听80,后面的是外面的 -d 命令行关闭,后台运行 /home/docker-nginx/nginx.conf:/etc/nginx/nginx.conf 把外面的配置地址复制到里面, 用了这么多nginx ,是因为 nginx 有一个server 还有http , 这还没配置log,就是把里面的log给同步到外面, 配置这么多可以写Dockerfile /home/lighthouse/out:/usr/share/nginx/html
这是把外面的静态文件扔到里面 /usr/share/nginx/html 这位置 nginx server 字段 location / root配置

http {
    gzip  on;  #要开 gzip压缩
}

server {
    listen  80;
    # listen 443 ssl;
    server_name  localhost;
    #access_log  /var/log/nginx/host.access.log  main;
    location / {
        root   /usr/share/nginx/html; #指向静态文件位置
        index  index.html index.htm;
        try_files $uri $uri.html $uri/ /index.html;
        add_header Access-Control-Allow-Origin *;
        add_header Cache-Control no-cache;
    }


    location = /50x.html {
        root   /usr/share/nginx/html;
    }
 
    # 可选:配置 HTTPS
    # listen 443 ssl;
    # ssl_certificate /path/to/your/ssl/certificate.crt;
    # ssl_certificate_key /path/to/your/ssl/private.key;
}

try_files $uri $uri.html $uri/ /index.html; 这里 try_files,SPA刷新路径失效就是 try_files 没有配置正确导致的

Cache-Control no-cache; 的意思是说每次使用缓存前,都需要请求,没有过期才能用。 Cache-Control 参数里面也有一点协商缓存的意思,Cache-Control 不止是强缓存

配置ssl需要证书,申请证书需要域名 申请后保持电话畅通,否则给你驳回还得重新申请 工信部将给你发送校验短信,请收到短信后24小时内按照短信提示登录工信部网站进行短信核验,超时未核验备案订单会被退回。短信24小时内最多重发3次,验证码有效期为24小时并且期间试错次数为5次。

nginx 解析域名和反向代理

server {
    listen  80;
    server_name  todos.asia www.todos.asia;

    location / {
        proxy_pass http://ip:3000; 
	    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;
    }
}

server {
    listen  80;
    server_name  b.com www.todos.asia;
    location / {
        proxy_pass http://ip:4000; 
   }
}

server_name 填写你购买的域名,有二级域名加空格

proxy_pass 就是访问80端口给代理到你自己的服务器3000端口来,这样的话,你的服务器可以有多个域名,然后代理到不同的端口

https 要监听443端口,默认是80

有了端口,防火墙也要把端口打开

本地的数据库和 node 版本

不要搞本地的,因为本地系统都特别旧,经常要你升级, node版本太低,你要安装各种C++的东西,烦的要死,数据库经常起不来,版本不对,为此都折腾了好几天。所以再也不搞本地的服务,都进docker

nextjs /app SSR

使用的是新版本,本来想学习下nextjs,官方给了一个todolist,我也就直接把官方给的拿来了。 里面有服务端渲染,服务端渲染最适合做本身就固定好的内容,个性化的内容不适合,需要用户登陆的请求统统不适合。

nextjs SSR 默认所有的页面都是服务端渲染, 服务端渲染只会渲染一次,不用使用useState 等。开始写惯了真的不习惯,老是不知道怎么写。RSC组件可以包含静态组件,静态组件不能包RSC组件,但是可以使用一些特殊的系法,让一些静态组件也能包含RSC组件。

SSR 部署的时候一直是node server.js 这样的命令行在跑。

SSR 页面不能被CDN,CDN只能缓存静态的页面

RSC form 使用 useForm 请求可以做到无js请求,基本就是直接操作数据库。

useForm 这个设置一个动态默认值也听麻烦的。比如用户id 需要请求才能得到,设置value,又提醒需要受控组件,action又是非受控组件,反正能运行但是一直warning 很烦人

踩坑: 直接看官方的视频,拿别人的例子来开发。

tailwind

学习了tailwind 的用法,同时也踩坑了一些复杂的写法比如 checked:before: 代表即是checked还是伪代码部分,如果需要一些动态的样式,不要拆开横,要把整个作为一个新变量 text-[#a9af99] text-[isRed: 'red':'green'],这种写法就是错误的。 flex 设置成flex-row 或者flex-col的时候 items 和 justify 是反着的。 使用一些tailwind框架UI 的时候,修改他们的样式,尤其是深层的样式, 通过这个好像是[.*_&]:text-white 选中所有的子属性,修改样式, 感觉覆盖样式还是很麻烦的

tailwind 框架 UI不好修改的时候,还不如自己写样式

数据库的速度

开始用的velcel默认的免费的数据库本地访问特别慢,跟代理没代理没关系,速度就是慢。然后用腾讯云自己的数据库一下快了

网站和接口一定要部署在一个地域,后来我网站部署到vercel的默认美国区域,访问的中国数据库,2s 的返回速度。尽管我又该回了vercel数据,在中国访问依然很慢,我不清楚其他国家慢还是vervel免费的数据库本身就不好用。

登录系统 next-auth jwt

使用next-auth ,建议直接看youtube 教学,先写好UI,数据,因为要做数据库,需要把用户名称存到库里,然后prisma 就出现了问题,各种魔改,总算保存在了库里,这个只能访问用户名各种基本信息,连jwt那个token都没有跟我生成,我还自己根据返回的json token 生成 一个jwt token,我发现依然需要借口,又写了一个接口,我还以为不需要接口了。 jwt就是编码带在请求头里发过去,那边解码解析。 cookie 或者localstorge是存储,不需要带 cookie 请求。

使用nextjs ssr 在腾讯云部署

官网的docker 部署版本过时了,我用的一个博客分享的

FROM node:18-alpine as base
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache g++ make py3-pip libc6-compat
WORKDIR /app
COPY package*.json ./
EXPOSE 3000

FROM base as builder
WORKDIR /app
COPY . .
RUN npm run build


FROM base as production
WORKDIR /app

ENV NODE_ENV=production
RUN npm ci

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs


COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/public ./public

CMD npm start

运行到 RUN apk add --no-cache g++ make py3-pip libc6-compat 一直过不去,我还在傻等着,后来才知道需要修改镜像,又添加了上面一行RUN sed -i

域名解析到腾讯云

本来使用 nginx 做了配置,启动一直访问不到,原来还需要域名备案,备案过程中电话要畅通,不然给你驳回,重新提交,他们会给你打电话,还有提交域名的时候,域名先不要解析,会让你删除解析记录再次提交

prisma

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
  accounts      Account[]
  sessions      Session[]
  todos         Todo[]

  @@map(name: "users")
}

@@map(name: "users") 不写的话 ,select * from user 查询不出来,这是我推断出来的,文档也没怎么看

model Todo {
  id        String   @id @default(cuid())
  content   String
  createdAt DateTime @default(now()) @map(name: "created_at")
  finished  Boolean  @default(false)
  user      User?    @relation(fields: [userId], references: [id])
  userId    String

  @@map(name: "todos")
}

user User? @relation(fields: [userId], references: [id]) 代码表示了Todo 数据模型中的 userId 字段与 User 数据模型中的 id 字段之间的关联关系。 也就是说,通过这个关系定义,Prisma 将会根据 userId 字段的数值来关联到 User 数据模型中相应的 id 字段,建立起这两个数据模型之间的连接。

const res = await prisma.todo.create({
      data: {
        content: data.todo,
        finished: false,
        user: {
          connect: {
            id: data.id
          }
        }
      },
    });

因为是关联表,创建的时候就要指定 id,写法可以是这样的

const todos = [{
     content: '',
     finished: false,
     userId:1
},{
     content: '',
     finished: false,
     userId:1
}]
const res = await prisma.todo.createMany({
    data: todos,
});

也可以这样批量创建

数据库一直自己删库

每天我创建了库,24小时后必定删库,开始第一天还以为遭到了入侵,朋友让我改密码。 后来每次我都发现库成了空。在部署的时候我终于想起了修改密码,但是密码修改名称修改,我什么都连不上了。 我试了很多次只修改密码不修改名称,开始以为密码太长,或者用了英文,各种瞎尝试,我发现只有密码是123456 的时候能连接成功。根据一些错误提示,通过搜索github有人说是因为外面有个数据库,里面有个数据库, 你连接的是外面的,我把外面的数据库全部卸载了,但是我修改密码还是连接不成功,于是我就在docker里面修改密码才成功,外面的那个配置密码是不对的。