diff --git a/README.md b/README.md index be5e91d65..bf7c30594 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 [![MacOS][MacOS-image]][download-url] [![Linux][Linux-image]][download-url] -[NextChatAI](https://nextchat.dev/chat) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [Web App](https://app.nextchat.dev) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Enterprise Edition](#enterprise-edition) / [Twitter](https://twitter.com/NextChatDev) [NextChatAI](https://nextchat.dev/chat) / [网页版](https://app.nextchat.dev) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) -[saas-url]: https://nextchat.dev/chat +[saas-url]: https://nextchat.dev/chat?utm_source=readme [saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge [web-url]: https://app.nextchat.dev/ [download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases diff --git a/README_CN.md b/README_CN.md index 640fe3933..73fbc3f51 100644 --- a/README_CN.md +++ b/README_CN.md @@ -8,7 +8,7 @@ 一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。 -[NextChatAI](https://nextchat.dev/chat) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) [Deploy on Zeabur](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Deploy on Zeabur](https://zeabur.com/templates/ZBUEFA) [Open in Gitpod](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) diff --git a/README_JA.md b/README_JA.md index ba3c514dc..416928c26 100644 --- a/README_JA.md +++ b/README_JA.md @@ -5,7 +5,7 @@ ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。 -[NextChatAI](https://nextchat.dev/chat) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) +[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N) [Zeaburでデプロイ](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [Zeaburでデプロイ](https://zeabur.com/templates/ZBUEFA) [Gitpodで開く](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web) diff --git a/app/app/(auth)/login/set-password/page.tsx b/app/app/(auth)/login/set-password/page.tsx index 2ee04158c..1e0f60ff9 100644 --- a/app/app/(auth)/login/set-password/page.tsx +++ b/app/app/(auth)/login/set-password/page.tsx @@ -35,7 +35,6 @@ export default function SetPasswordPage() { const onFinish: FormProps["onFinish"] = (values) => { // setLoading(true); // console.log('-------------', values) - // @ts-expect-error fetch(`/api/user/${session?.user?.id}`, { method: "PUT", credentials: "include", @@ -61,31 +60,28 @@ export default function SetPasswordPage() { layout="vertical" onFinish={onFinish} > - { - // @ts-expect-error - status === "authenticated" && session?.user?.hasPassword && ( - { - if (!value) { - return Promise.reject(new Error("请填写该字段")); - } - }, + {status === "authenticated" && session?.user?.hasPassword && ( + { + if (!value) { + return Promise.reject(new Error("请填写该字段")); + } }, - ]} - > - } - type="password" - autoComplete="current-password" - id="user_old_password" - /> - - ) - } + }, + ]} + > + } + type="password" + autoComplete="current-password" + id="user_old_password" + /> + + )} { - // @ts-expect-error if (!value?.user?.hasPassword) { if (result_url === "/") { result_url = "/login/set-password"; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 29091aced..c50905d2e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -438,14 +438,23 @@ function useScrollToBottom( if (dom) { requestAnimationFrame(() => { setAutoScroll(true); - dom.scrollTo(0, dom.scrollHeight); + // dom.scrollTo(0, dom.scrollHeight); + // 丝滑一点 + dom.scrollTo({ + top: dom.scrollHeight, + behavior: "smooth", + }); }); } } // auto scroll useEffect(() => { - if (autoScroll && !detach) { + // if (autoScroll && !detach) { + // scrollDomToBottom(); + // } + // 自动滚动一直有bug,直接强制修改了 + if (autoScroll) { scrollDomToBottom(); } }); diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index db4d757b4..3df9d00fb 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -253,7 +253,7 @@ function tryWrapHtmlCode(text: string) { }, ) .replace( - /(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*?)([`]*?)([\n\r]*?)/g, + /(<\/body>)([\r\n\s]*?)(<\/html>)([\n\r]*)([`]*)([\n\r]*?)/g, (match, bodyEnd, space, htmlEnd, newLine, quoteEnd) => { return !quoteEnd ? bodyEnd + space + htmlEnd + "\n```\n" : match; }, diff --git a/chat.conf b/chat.conf deleted file mode 100644 index 1a5c8d26e..000000000 --- a/chat.conf +++ /dev/null @@ -1,88 +0,0 @@ -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} - - # 匹配以 www. 开头的请求并重定向到 HTTPS 的不带 www. - server { - listen 80; - listen 443 ssl; - server_name ~^www\.(?.+)$; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_certificate "/root/cert.pem"; - ssl_certificate_key "/root/cert.key"; - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 10m; - ssl_ciphers PROFILE=SYSTEM; - ssl_prefer_server_ciphers on; - add_header Strict-Transport-Security "max-age=31536000"; - - return 301 https://$domain$request_uri; - } - - # 匹配任意 HTTP 重定向到 HTTPS - server { - listen 80 default_server; - server_name _; - - # 将 HTTP 请求重定向到 HTTPS - return 301 https://$host$request_uri; - } - - - -server { - listen 443 ssl http2 default_server; - server_name _; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_certificate "/root/cert.pem"; - ssl_certificate_key "/root/cert.key"; - ssl_session_cache shared:SSL:1m; - ssl_session_timeout 10m; - ssl_ciphers PROFILE=SYSTEM; - ssl_prefer_server_ciphers on; - add_header Strict-Transport-Security "max-age=31536000"; - - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - - -#REWRITE-START -if ($host ~* ^www\.(.+)$) { - set $new_host \$1; - return 301 https://$new_host$request_uri; -} -#REWRITE-END - -#PROXY-START/ - -location ^~ / -{ - proxy_pass http://127.0.0.1:23000; - 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 REMOTE-HOST $remote_addr; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_http_version 1.1; - - # 禁止缓存的相关设置 - proxy_buffering off; - proxy_no_cache 1; - proxy_cache_bypass 1; - add_header X-Accel-Buffering "no"; - # 设置代理超时 - proxy_read_timeout 24h; - proxy_connect_timeout 3m; - proxy_send_timeout 24h; - proxy_max_temp_file_size 0; -} - -#PROXY-END/ - - -} - diff --git a/lib/auth.ts b/lib/auth.ts index b698f82f4..7562a60a8 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -9,6 +9,9 @@ import { isEmail, isName } from "@/lib/auth_list"; import {createTransport} from "nodemailer"; import { comparePassword } from "@/lib/utils"; import { randomBytes } from "crypto"; +import { type Session } from "next-auth"; +import { type JWT } from "next-auth/jwt"; + const SECURE_COOKIES:boolean = !!process.env.SECURE_COOKIES; let verificationTokens = new Map(); @@ -153,25 +156,23 @@ export const authOptions: NextAuthOptions = { if (user) { token.user = user; } else { - const updateUser = await prisma.user.findUnique({ where: { id: token.sub }}); - // console.log('========', updateUser) + const updateUser: User | null = await prisma.user.findUnique({ where: { id: token.sub }}); if (!updateUser || !updateUser.allowToLogin) { throw new Error('无法刷新令牌,用户状态不正确'); } - token.user = updateUser; + token.user = updateUser as User; } return token; }, - session: async ({ session, token }) => { + session: async ({ session, token }: { + session: Session, + token: JWT + }) => { session.user = { ...session.user, - // @ts-expect-error - id: token?.sub, - // @ts-expect-error + id: token?.sub ?? "", username: token?.user?.username || token?.user?.gh_username, - // @ts-expect-error hasPassword: !!token?.user?.password, - // @ts-expect-error isAdmin: token?.user?.isAdmin, }; // console.log('555555555,', session, token) diff --git a/lib/auth_client.ts b/lib/auth_client.ts index 143480963..5b65b200f 100644 --- a/lib/auth_client.ts +++ b/lib/auth_client.ts @@ -1,21 +1,21 @@ import { isName } from "@/lib/auth_list"; -import { CUS_JWT } from "@/lib/auth_type"; +import { type JWT } from "next-auth/jwt"; -export async function VerifiedUser(session: CUS_JWT | null) { +export async function VerifiedUser(session: JWT | null) { const userId = session?.sub const name = session?.email || session?.name return !!(name && isName(name) && userId); } -export async function VerifiedAdminUser(session: CUS_JWT | null) { +export async function VerifiedAdminUser(session: JWT | null) { // console.log('-------', session, session?.user?.isAdmin) return !!session?.user?.isAdmin; // const name = session?.email || session?.name // return !!(name && ADMIN_LIST.includes(name)); } -export function VerifiedNeedSetPassword(path: string, session: CUS_JWT | null,) { +export function VerifiedNeedSetPassword(path: string, session: JWT | null,) { const need_set_pwd = !session?.user?.password return path === "/login/set-password" && need_set_pwd; } diff --git a/lib/auth_type.ts b/lib/auth_type.ts deleted file mode 100644 index 28623eb73..000000000 --- a/lib/auth_type.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { JWT } from "next-auth/jwt"; -import { User } from "@prisma/client"; - -export type CUS_JWT = JWT & { - user: User, -} diff --git a/lib/types/next-auth.d.ts b/lib/types/next-auth.d.ts new file mode 100644 index 000000000..f062194b2 --- /dev/null +++ b/lib/types/next-auth.d.ts @@ -0,0 +1,45 @@ +// types/next-auth.d.ts +import { DefaultSession, DefaultUser } from "next-auth"; + + +declare module "next-auth" { + /** + * 扩展 Session 接口,添加自定义的用户属性 + */ + interface Session { + user: { + id: string; + username?: string | null; + hasPassword?: boolean | null; + isAdmin?: boolean | null; + } & DefaultSession["user"]; + } + + /** + * 扩展 User 接口,添加自定义属性 + * 注意:保持属性可选,以与 AdapterUser 兼容 + */ + interface User extends DefaultUser { + id: string; + username?: string; + gh_username?: string; + password?: string; + isAdmin?: boolean; + } + +} + +declare module "next-auth/jwt" { + /** + * 扩展 JWT 接口,添加自定义的用户属性 + */ + interface JWT { + user?: { + id: string; + username?: string | null; + gh_username?: string | null; + password?: string | null; + isAdmin?: boolean | null; + }; + } +} diff --git a/middleware.ts b/middleware.ts index 1a8c7ff79..745b4314f 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,9 +1,7 @@ import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { getToken } from "next-auth/jwt"; -import { VerifiedUser, VerifiedAdminUser, VerifiedNeedSetPassword } from "@/lib/auth_client"; -import { CUS_JWT } from "@/lib/auth_type"; - +import { VerifiedUser, VerifiedAdminUser } from "@/lib/auth_client"; export default async function middleware(req: NextRequest) { const url = req.nextUrl; @@ -17,8 +15,8 @@ export default async function middleware(req: NextRequest) { } const session = await getToken({ req }); - const isUser = await VerifiedUser(session as CUS_JWT); - const isAdminUser = await VerifiedAdminUser(session as CUS_JWT); + const isUser = await VerifiedUser(session); + const isAdminUser = await VerifiedAdminUser(session); // console.log('----session', session, '---isUser', isUser, '---isAdmin', isAdminUser) // 管理员页面的api接口还是要认证的 if (path.startsWith('/api/admin/')) { @@ -45,7 +43,7 @@ export default async function middleware(req: NextRequest) { ); } - // if (VerifiedNeedSetPassword(path, session as CUS_JWT)) { + // if (VerifiedNeedSetPassword(path, session)) { // console.log('-0-0-- 需要修改密码', ) // // return NextResponse.redirect(new URL("/login/set-password", req.url)) // } diff --git a/tsconfig.json b/tsconfig.json index c73eef3e8..ce1a04b38 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ ], "paths": { "@/*": ["./*"] - } + }, + "typeRoots": ["lib/types"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/calcTextareaHeight.ts"], "exclude": ["node_modules"]