feat: upgrade to Next 16 (#9851)

* upgrade next 16

* try to fix

* try to fix

* upgrade

* fix sitemap build

* try to fix build

* try to fix build with next 16

* fix docker permission

* 🔒 fix(ci): fix code injection vulnerability and permissions in docker workflow

- Add pull-requests: write permission to allow PR comments
- Fix code injection vulnerability by using env variables instead of direct interpolation
- Prevent potential security risks from malicious branch names in pull_request_target events

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* 🔧 chore(ci): change desktop pr build to use pull_request_target

- Change from pull_request to pull_request_target to access secrets and write permissions
- Update permissions from read-all to specific write permissions for contents and pull-requests
- This allows PR builds to create releases and comment on PRs from forks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* add comment

* fix on

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Arvin Xu
2025-11-02 00:53:13 +08:00
committed by GitHub
parent 39a7399765
commit abb71ec846
12 changed files with 60 additions and 40 deletions

View File

@@ -2,6 +2,7 @@ name: Desktop PR Build
on:
pull_request:
pull_request_target:
types: [synchronize, labeled, unlabeled] # PR 更新或标签变化时触发
# 确保同一时间只运行一个相同的 workflow取消正在进行的旧的运行
@@ -10,7 +11,9 @@ concurrency:
cancel-in-progress: true
# Add default permissions
permissions: read-all
permissions:
contents: write
pull-requests: write
env:
PR_TAG_PREFIX: pr- # PR 构建版本的前缀标识

View File

@@ -1,12 +1,14 @@
name: Publish Docker Image
permissions:
contents: read
pull-requests: write
on:
workflow_dispatch:
release:
types: [published]
pull_request:
pull_request_target:
types: [synchronize, labeled, unlabeled]
concurrency:
@@ -52,9 +54,10 @@ jobs:
- name: Generate PR metadata
if: github.event_name == 'pull_request'
id: pr_meta
env:
BRANCH_NAME: ${{ github.head_ref }}
run: |
branch_name="${{ github.head_ref }}"
sanitized_branch=$(echo "${branch_name}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g')
sanitized_branch=$(echo "${BRANCH_NAME}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g')
echo "pr_tag=${sanitized_branch}-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Docker meta
@@ -131,9 +134,10 @@ jobs:
- name: Generate PR metadata
if: github.event_name == 'pull_request'
id: pr_meta
env:
BRANCH_NAME: ${{ github.head_ref }}
run: |
branch_name="${{ github.head_ref }}"
sanitized_branch=$(echo "${branch_name}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g')
sanitized_branch=$(echo "${BRANCH_NAME}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g')
echo "pr_tag=${sanitized_branch}-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Docker meta

View File

@@ -1,6 +1,6 @@
// copy from https://github.com/kirill-konshin/next-electron-rsc
import { serialize as serializeCookie } from 'cookie';
import { type Protocol, type Session, protocol } from 'electron';
import { type Protocol, type Session } from 'electron';
import type { NextConfig } from 'next';
import type NextNodeServer from 'next/dist/server/next-server';
import assert from 'node:assert';
@@ -202,6 +202,11 @@ export function createHandler({
if (!isDev) {
logger.info('Initializing Next.js app for production');
// https://github.com/lobehub/lobe-chat/pull/9851
// @ts-expect-error
// noinspection JSConstantReassignment
process.env.NODE_ENV = 'production';
const next = require(resolve.sync('next', { basedir: standaloneDir }));
// @see https://github.com/vercel/next.js/issues/64031#issuecomment-2078708340
@@ -209,10 +214,7 @@ export function createHandler({
.config as NextConfig;
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(config);
app = next({
dev: false,
dir: standaloneDir,
}) as NextNodeServer;
app = next({ dir: standaloneDir }) as NextNodeServer;
handler = app.getRequestHandler();
preparePromise = app.prepare();

View File

@@ -28,9 +28,6 @@ const nextConfig: NextConfig = {
emotion: true,
},
compress: isProd,
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
optimizePackageImports: [
'emoji-mart',
@@ -46,6 +43,7 @@ const nextConfig: NextConfig = {
// so we need to disable it
// refs: https://github.com/lobehub/lobe-chat/pull/7430
serverMinification: false,
turbopackFileSystemCacheForDev: true,
webVitalsAttribution: ['CLS', 'LCP'],
webpackBuildWorker: true,
webpackMemoryOptimizations: true,
@@ -273,6 +271,7 @@ const nextConfig: NextConfig = {
// when external packages in dev mode with turbopack, this config will lead to bundle error
serverExternalPackages: isProd ? ['@electric-sql/pglite', "pdfkit"] : ["pdfkit"],
transpilePackages: ['pdfjs-dist', 'mermaid'],
turbopack: {},
typescript: {
ignoreBuildErrors: true,

View File

@@ -31,14 +31,14 @@
],
"scripts": {
"prebuild": "tsx scripts/prebuild.mts && npm run lint",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=6144 next build",
"build": "cross-env NODE_OPTIONS=--max-old-space-size=6144 next build --webpack",
"postbuild": "npm run build-sitemap && npm run build-migrate-db",
"build-migrate-db": "bun run db:migrate",
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
"build:analyze": "NODE_OPTIONS=--max-old-space-size=6144 ANALYZE=true next build",
"build:docker": "npm run prebuild && NODE_OPTIONS=--max-old-space-size=6144 DOCKER=true next build && npm run build-sitemap",
"build:analyze": "NODE_OPTIONS=--max-old-space-size=6144 ANALYZE=true next build --webpack",
"build:docker": "npm run prebuild && NODE_OPTIONS=--max-old-space-size=6144 DOCKER=true next build --webpack && npm run build-sitemap",
"prebuild:electron": "cross-env NEXT_PUBLIC_IS_DESKTOP_APP=1 tsx scripts/prebuild.mts",
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1 NEXT_PUBLIC_SERVICE_MODE=server next build",
"build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1 NEXT_PUBLIC_SERVICE_MODE=server next build --webpack",
"clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'",
"db:generate": "drizzle-kit generate && npm run db:generate-client && npm run workflow:dbml",
"db:generate-client": "tsx ./scripts/migrateClientDB/compile-migrations.ts",
@@ -50,9 +50,9 @@
"desktop:build-local": "npm run desktop:build-next && npm run desktop:prepare-dist && npm run build-local --prefix=./apps/desktop",
"desktop:build-next": "npm run build:electron",
"desktop:prepare-dist": "tsx scripts/electronWorkflow/moveNextStandalone.ts",
"dev": "next dev --turbopack -p 3010",
"dev:desktop": "next dev --turbopack -p 3015",
"dev:mobile": "next dev --turbopack -p 3018",
"dev": "next dev -p 3010",
"dev:desktop": "next dev -p 3015",
"dev:mobile": "next dev -p 3018",
"docs:i18n": "lobe-i18n md && npm run lint:md && npm run lint:mdx && prettier -c --write locales/**/*",
"docs:seo": "lobe-seo && npm run lint:mdx",
"e2e": "cd e2e && npm run test:smoke",
@@ -123,9 +123,6 @@
"eslint --fix"
]
},
"overrides": {
"mdast-util-gfm-autolink-literal": "2.0.0"
},
"dependencies": {
"@ant-design/icons": "^5.6.1",
"@ant-design/pro-components": "^2.8.10",
@@ -174,7 +171,7 @@
"@lobehub/ui": "^2.13.2",
"@modelcontextprotocol/sdk": "^1.20.0",
"@neondatabase/serverless": "^1.0.2",
"@next/third-parties": "^15.5.4",
"@next/third-parties": "^16.0.1",
"@opentelemetry/exporter-jaeger": "^2.1.0",
"@opentelemetry/winston-transport": "^0.18.0",
"@react-pdf/renderer": "^4.3.0",
@@ -235,7 +232,7 @@
"model-bank": "workspace:*",
"modern-screenshot": "^4.6.6",
"nanoid": "^5.1.6",
"next": "~15.3.5",
"next": "^16.0.1",
"next-auth": "5.0.0-beta.30",
"next-mdx-remote": "^5.0.0",
"nextjs-toploader": "^3.9.17",
@@ -261,9 +258,9 @@
"pwa-install-handler": "^2.6.3",
"query-string": "^9.3.1",
"random-words": "^2.0.1",
"react": "^19.2.0",
"react": "19.2.0",
"react-confetti": "^6.4.0",
"react-dom": "^19.2.0",
"react-dom": "19.2.0",
"react-fast-marquee": "^1.6.5",
"react-hotkeys-hook": "^5.1.0",
"react-i18next": "^15.7.4",
@@ -313,8 +310,8 @@
"@lobehub/lint": "^1.26.2",
"@lobehub/market-types": "^1.11.4",
"@lobehub/seo-cli": "^1.7.0",
"@next/bundle-analyzer": "^15.5.4",
"@next/eslint-plugin-next": "^15.5.4",
"@next/bundle-analyzer": "^16.0.1",
"@next/eslint-plugin-next": "^15.5.6",
"@peculiar/webcrypto": "^1.5.0",
"@playwright/test": "^1.49.1",
"@prettier/sync": "^0.6.1",
@@ -335,8 +332,8 @@
"@types/oidc-provider": "^9.5.0",
"@types/pdfkit": "^0.17.3",
"@types/pg": "^8.15.5",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.1",
"@types/react": "19.2.2",
"@types/react-dom": "19.2.2",
"@types/rtl-detect": "^1.0.3",
"@types/semver": "^7.7.1",
"@types/systemjs": "^6.15.3",
@@ -399,9 +396,6 @@
"pnpm": {
"onlyBuiltDependencies": [
"@vercel/speed-insights"
],
"overrides": {
"mdast-util-gfm-autolink-literal": "2.0.0"
}
]
}
}

View File

@@ -3,5 +3,8 @@
"version": "1.0.0",
"private": true,
"main": "src/index.ts",
"types": "src/index.ts"
"types": "src/index.ts",
"peerDependencies": {
"react": "^19"
}
}

View File

@@ -28,6 +28,7 @@
"@napi-rs/canvas": "^0.1.70",
"@xmldom/xmldom": "^0.9.8",
"concat-stream": "^2.0.0",
"debug": "^4.3.4",
"mammoth": "^1.8.0",
"officeparser": "5.1.1",
"pdfjs-dist": "4.10.38",

View File

@@ -18,7 +18,7 @@ export const GET = async (request: NextRequest) => {
return NextResponse.json('tag query parameter is required', { status: 400 });
}
revalidateTag(tag);
revalidateTag(tag, 'max');
return Response.json({ now: Date.now(), revalidated: true });
};

View File

@@ -73,6 +73,7 @@ export const generateViewport = async (props: DynamicLayoutProps): ResolvingView
return {
...dynamicScale,
colorScheme: null,
initialScale: 1,
minimumScale: 1,
themeColor: [

View File

@@ -47,7 +47,13 @@ export function parsePaginatedId(id: string): { page?: number; type: SitemapType
return { type: id as SitemapType };
}
export default async function sitemap({ id }: { id: string }): Promise<MetadataRoute.Sitemap> {
export default async function sitemap({
id: idPromise,
}: {
id: string;
}): Promise<MetadataRoute.Sitemap> {
const id = await idPromise;
const { type, page } = parsePaginatedId(id);
const sitemapModule = new Sitemap();

View File

@@ -13,7 +13,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"baseUrl": ".",
"types": ["vitest/globals", "@serwist/next/typings"],
@@ -41,5 +41,12 @@
".temp",
"e2e"
],
"include": ["**/*.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "next-env.d.ts"]
"include": [
"**/*.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"next-env.d.ts",
".next/dev/types/**/*.ts"
]
}