fullstack-matters-next-全栈框架搭建过程中遇到的问题记录

分割线

config

Image

⚠ The “images.domains” configuration is deprecated. Please use “images.remotePatterns” configuration instead.

const config = {
webpack: (config, { isServer }) => {
},

// https://nextjs.org/docs/messages/next-image-unconfigured-host
// https://stackoverflow.com/questions/71235874/how-to-allow-all-domains-for-image-nextjs-config
images: {
remotePatterns: [
{ protocol: "https", hostname: "**" },
{ protocol: "https", hostname: "xxx.com" },
],

// deprecated
// domains: [""],
},
};

next-auth

google-timeout

GoogleProvider({
clientId: env.GOOGLE_ID,
clientSecret: env.GOOGLE_SECRET,
httpOptions: {
timeout: 40000,
},
}),
[next-auth][error][SIGNIN_OAUTH_ERROR]
https://next-auth.js.org/errors#signin_oauth_error outgoing request timed out after 3500ms {
error: {
message: 'outgoing request timed out after 3500ms',
stack: 'RPError: outgoing request timed out after 3500ms\n' +
' at eval (webpack-internal:///(rsc)/./node_modules/.pnpm/openid-client@5.6.5/node_modules/openid-client/lib/helpers/request.js:140:13)\n' +
...
' at async Server.requestListener (D:\\Repos\\AI-MOBI\\Dress\\ChatUp-AI-Dress-fullstack\\node_modules\\.pnpm\\next@14.2.5_react-dom@18.3.1_react@18.3.1__react@18.3.1\\node_modules\\next\\dist\\server\\lib\\start-server.js:141:13)',
name: 'RPError'
},
providerId: 'google',
message: 'outgoing request timed out after 3500ms'
}


https://next-auth.js.org/errors#signin_oauth_error {
error: {
message: '',
stack: 'AggregateError\n' +
' at internalConnectMultiple (node:net:1114:18)\n' +
' at afterConnectMultiple (node:net:1667:5)\n' +
' at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17)',
name: 'AggregateError'
},
providerId: 'google',
message: ''
}

这是因为 auth 走的是后端而不是前端浏览器, next dev 跑的服务没走网络代理, 打开 tun 模式就行了


secret-missing

Please define a secret in production

providers: [
GoogleProvider({
clientId: env.GOOGLE_ID,
clientSecret: env.GOOGLE_SECRET,
httpOptions: {
timeout: 40000,
},
}),
],
// add this
secret: env.NEXTAUTH_SECRET,

分割线

prisma

client-deps-error

Prisma Client was generated for “linux-musl-openssl-3.0.x”, but the actual deployment required “debian-openssl-3.0.x”

如下加上 [3]

generator client {
provider = "prisma-client-js"
// for dockerfile doploy
binaryTargets = ["native", "debian-openssl-3.0.x"]
}

table-does-not-exist

The table main.account does not exist in the current database.

类似的问题先直接试一下 prisma db push 更新下数据库结构


nextauth-mysql

Try signing in with a different account.
next auth google login failed | prisma | mysql

非常坑的问题, 说是无法登录, 实际是无法在 db 中插入数据, 务必删库重新建一个试试!!

too-lang

NextAuth With Prisma for mysql specified key is too long

字段加上 @db.Text

分割线

usage

文件上传-upload-file-image

如何实现图片/文件上传操作

使用 t3app 创建的项目, 其中用到了 tRPC, 但很难受的是它对文件传输不支持

只能用 next 原生 API router 去写 RESTFUL [1]

form-data-parse

next 后端如何解析 form 格式带文件的请求

一开始是想用 form 直接上传图片文件, 但是解析过程中有问题 (用到的是 formidable )

不知道哪里的问题, 解析出来的文件会在 fields 里 (string 形式, 很奇怪), files 为空

export const config = {
api: {
// 需要禁用默认的
bodyParser: false,
},
};

// 将 form.parse 封装在一个 Promise 中
function parseForm(
req: NextApiRequest
): Promise<{ fields: Fields; files: Files }> {
return new Promise((resolve, reject) => {
const form = new IncomingForm({
allowEmptyFiles: true,
multiples: true,
maxFileSize: 50 * 1024 * 1024, // 增加文件大小限制, 单位为字节
});

form.parse(req, (err, fields, files) => {
if (err) {
reject(err); // 如果解析出错,reject 这个 promise
} else {
resolve({ fields, files }); // 如果成功,resolve 这个 promise,返回解析后的数据
}
});
});
}

image-to-Buffer2string

转为使用 string 去传递图片

前端, 先从事件拿到 File 对象, 转为 ArrayBuffer, 再转为 binary 格式的 Buffer

const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;

file
.arrayBuffer()
.then((data) => {
const textData = Buffer.from(data).toString("binary");

后端:

interface ResData {
file: string;
}

// API路由处理程序
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}

try {
const data = JSON.parse(req.body as string) as ResData;
// console.log(data);

// 将接收到的字符串转为 Buffer
const buffer = Buffer.from(data.file, "binary");

// TODO: 保存或者处理 Buffer

return res.status(200).json({ status: "ok" });
} catch (err) {
// 捕获解析过程中可能发生的错误
console.error(err);
return res.status(500).json({ error: "Error parsing form data" });
}
}

next-router-多页面路由

使用 t3app 创建的项目一般会带有 layout.tsx 和 page.tsx, 作用是这样:

- app
- layout.tsx // 控制网站根节点页面布局
- page.tsx // 控制 '/' 页面路由
- login // 新建 '/login' 页面
- layout.tsx// 同理, 根节点必须有, 子页面没有也行
- page.tsx // 同理, 子页面必须
- about // 新建 '/about' 页面
- page.tsx

可以新建这个文件, 然后访问 /login 试一试

// `app/login/page.tsx` is the UI for the `/login` URL
export default function Page() {
return <h1>Hello, login page!</h1>;
}

payment-stripe

No such price: ‘price_1PyqB006uMozVQ0qomnghCIo’; a similar object exists in live mode, but a test mode key was used to make this request.

priceId 与 stripe secret 不匹配导致, 正式与测试 key 不通用

分割线

deployments

vercel-file-read-write

用了之后才深刻知道 vercel 是个 serverless 平台, 也就是代码里不能用 “fs”

当然也不是完全不能用, 是只读状态 (申请写入会报错: EROFS: read-only file system)

文件引入也比较苛刻 (通过 ../ 这种导入很容易出现 file not found) [2]

按官方建议 是这种:

path.join(process.cwd(), "/public/img/xxx.png");

如果真遇到需要 临时 写入的情况, 可以用 /tmp 这个路径

vercel read-only file system, chmod

另外更建议的做法是用 Buffer 传递, 一般主流第三方库的输入输入都适配了 (比如我用到了 Jimp)

Jimp-watermark-添加水印

const srcImg = await Jimp.read(
Buffer.from(await(await fetch(data.output.image_url ?? "")).arrayBuffer())
);
// 水印加到左下角
srcImg.composite(
await Jimp.read(path.join(process.cwd(), "/public/img/watermark.png")),
10,
srcImg.getHeight() - 100,
{
mode: Jimp.BLEND_SOURCE_OVER,
opacitySource: 1,
opacityDest: 0.8,
}
);

const resultBuffer = await srcImg.getBufferAsync(Jimp.MIME_JPEG);

aliyun-百炼换装

Download the media resource timed out during the data inspection process.

这问题是由于资源链接离上海有点远/太大, 获取超时了, 把文件服务换到 ap-shanghai


centos7-宝塔-docker-CICD

首先面临的问题是 centos 7 在不升级系统的情况下无法直接安装 nodejs 18 (nextjs 最低要求)

所以选择用 docker 部署, t3app 自带了 Dockerfile

实践-CICD-nextjs-docker-dotenv

一般情况下前端项目的环境变量不太会在 build 时使用, 如果在代码里写环境变量切换 (JS/TS), 会在 build 时被编译为静态的, 无法在 run 的时候动态变化

所以用 docker 编译 nextjs 项目时, dockerfile 不用考虑 .env, 也不建议将 .env 打包在 docker image 中

build 为一个 docker image, 把 .env 复制到不同环境再运行


双引号问题

.env 中正常设置了: NEXTAUTH_URL="http://localhost:3000", 但运行时报错了

❌ Invalid environment variables: { NEXTAUTH_URL: [ ‘Invalid url’ ] }

实际 .env 的解析也跟部署环境有联系, 我用的是 docker run --env-file .env 的形式插入环境变量, docker 中实际是这样解析的

"25" : "NEXTAUTH_URL="http://localhost:3000"",
"26" : "NEXT_PUBLIC_NEXTAUTH_URL="http://localhost:3000"",
"27" : "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"28" : "SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt",
"29" : "NEXT_SHARP_PATH=/app/node_modules/sharp"

但可见的问题是引号被提前截止, 所以导致程序无法引到变量

分割线

借物表

[1]: https://stackoverflow.com/questions/76105855/send-blob-image-from-frontend-to-backend-with-nextjs-and-trpc-t3-stack

[2]: How to Read and Write Files in Next.js on a Vercel Deployed Website

[3]: MissingSecret [MissingSecretError]: Please define a secret in production