我正在为同一网站的不同子域构建多个Nextjs应用程序。我们有一个REST API后端,它有一个app-info
端点,它给了我一些关于SEO和其他东西的重要信息。我需要在服务器端呈现这些数据。问题是这些数据不会经常改变(如果需要的话,它们会由管理员更新),所以没有必要在每个页面上使用getServerSideProps
或App.getInitialProps
。我只需要每小时调用这个端点,并根据这些数据创建/更新一个json文件。我怎样才能达到这样的行为?顺便说一下,我没有使用Vercel
部署这些站点,我们使用我们自己的服务器。
我也试过这个教程,但它没有正常工作。它确实成功运行了worker.js
文件,但我无法在浏览器中打开网站。在next.config.js
中添加webpack
部分后,网站停止工作。
注意:我需要访问环境变量,所以我不能使用这个解决方案。由于我们有多个子域,每个子域都有一个测试和生产服务器,所以我很难使用cron-job.org或类似的解决方案。我希望能够与Next js一起拥有cron作业,并使用单个命令(npm start
例如运行作业和Next js服务器)运行它
在玩了一段时间后,我想出了一个脚本,可能看起来不理想,但这是我能得到的最接近我想要实现的:
// cron-build.js
const { exec, spawn } = require("child_process")
const fs = require("fs-extra")
require("dotenv").config({
path: "./.env.production.local"
})
const isEqual = require("lodash.isequal")
const cron = require("node-cron")
const execPromise = (command) =>
new Promise((resolve) => {
exec(command, (err, stdout, stderr) => {
resolve({ err, stdout, stderr })
})
})
/**
* fetchs app info and then starts build procedure if appInfo.json does not exist or it differs from server.
*/
const build = async () => {
try {
console.log("fetching app infon")
const appInfo = await fetchAppInfo()
let currentAppInfo = undefined
try {
currentAppInfo = await fs.readJSON("./src/appInfo.json")
} catch (e) {
}
if (isEqual(appInfo, currentAppInfo)) {
console.log("No change found in app infon")
rest(true)
return
}
fs.writeJson("./src/appInfo.json", appInfo)
} catch (e) {
console.log(e)
throw e
}
console.log("Change detected in app info. Rebuilding application...n")
const buildProcess = spawn("npm", ["run", "build"])
buildProcess.addListener("error", (data) => console.log("error", data))
buildProcess.on("exit", (exitCode) => {
if (exitCode === 0) {
console.log("Successful build. Restarting server...n")
rest()
} else {
console.error(
"Build failed. Run `npm run build` in your terminal to see the logs"
)
}
})
buildProcess.on("message", (data) => console.log("message", data))
buildProcess.stdout.on("data", (data) => console.log(`${data}`))
}
/**
* The rest of the build process (killing current running server and restarting server)
* @param {boolean} noRestart If set to true, only starts the server and won't kill already running server
* @returns
*/
const rest = async (noRestart) => {
const { err: err2, stdout } = await execPromise(
`sudo ss -lptn 'sport = :${process.env.SERVER_PORT || 8080}'`
)
if (err2) {
console.log(err2)
return
}
const pid = stdout
.toString()
.match(/pid=d+/)?.[0]
?.split("=")?.[1]
if (pid) {
if (noRestart) return
const { err } = execPromise(`sudo kill ${pid}`)
if (err) {
console.log("failed to kill current running server. error:", err)
return
}
}
const server = spawn("npx", ["next", "start", "-p", process.env.SERVER_PORT || 8080])
server.stdout.on("data", (data) => console.log(`${data}`))
server.stdout.on("error", (data) => console.log(`${data}`))
server.on("close", () => server.removeAllListeners())
}
build()
cron.schedule("0 0 * * * *", build)
这个脚本从后端获取app-info
,如果数据与使用exec
和spawn
的当前数据不同,将重建项目(事实上,我手动从Node js
运行下一个脚本)。使用sudo node cron-build.js
运行此脚本,您可以简单地将输出json文件导入到所有组件和页面中,因为该数据在构建时可用,因此将在项目中编译。