From 0090ffd3bbe83a99a4de15e733619569cd78a69b Mon Sep 17 00:00:00 2001 From: 14790897 <14790897abc@gmail.com> Date: Wed, 13 Mar 2024 20:51:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90linuxdo=20oauth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.local | 5 +- .env.production | 3 + app/[lng]/login/page.tsx | 2 + app/[lng]/page.tsx | 1 + app/api/oauth/callback/route.ts | 106 ++++++++++++++++++++++++++++++++ components/LinuxdoSignin.tsx | 28 +++++++++ middleware.ts | 2 +- utils/supabase/serverutils.ts | 47 ++++++++++++++ utils/supabase/supabaseutils.ts | 1 + 9 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 app/api/oauth/callback/route.ts create mode 100644 components/LinuxdoSignin.tsx create mode 100644 utils/supabase/serverutils.ts diff --git a/.env.local b/.env.local index 742678a..33e0e13 100644 --- a/.env.local +++ b/.env.local @@ -12,4 +12,7 @@ NEXT_PUBLIC_PAPER_URL=/api/paper #"https://api.openai.com/v1/chat/completions" "https://api.liuweiqing.top" "https://api.liuweiqing.top/v1/chat/completions" #node转发设置为 /api/v1/chat/completions https://one.caifree.com sk-aiHrrRLYUUelHstX69E9484509254dBf92061d6744FfFaD1 VERCEL_URL=https://www.paperai.life -NODE_ENV=development \ No newline at end of file +NODE_ENV=development +CLIENT_ID=UrgIEI0n03tveTmaOV0IU8qRY4DttGY4 +CLIENT_SECRET=ljShbIlIrfULu4BTUVTT4azeR90PtAif +REDIRECT_URI=http://localhost:3000/api/oauth/callback \ No newline at end of file diff --git a/.env.production b/.env.production index 9a7a8b3..bd4e5d0 100644 --- a/.env.production +++ b/.env.production @@ -7,3 +7,6 @@ NEXT_PUBLIC_SEMANTIC_API_KEY=hEQvK6ARe84dzDPcMnpzX4n9jfoqztkMfaftPWnb NEXT_PUBLIC_PUBMED_API_KEY=057616e7ce6c722f2ae8679e38a8be9b1a09 VERCEL_URL=https://www.paperai.life NODE_ENV=production +CLIENT_ID=RcgInz3KqEhb2KdW2yg5WUgAf3KHcJAC +CLIENT_SECRET=U4z8TgPIV1GWCXhFFNEVQyfmDotf91K6 +REDIRECT_URI=https://www.paperai.life/api/oauth/callback \ No newline at end of file diff --git a/app/[lng]/login/page.tsx b/app/[lng]/login/page.tsx index 85ccf5b..3fe6d7b 100644 --- a/app/[lng]/login/page.tsx +++ b/app/[lng]/login/page.tsx @@ -12,6 +12,7 @@ import { FooterBase } from "@/components/Footer/FooterBase"; import { insertUserProfile } from "@/utils/supabase/supabaseutils"; // SignInWithProvider import { SignInWithProvider } from "@/components/SignInWithProvider"; +import LinuxdoSignin from "@/components/LinuxdoSignin"; export default async function Login({ searchParams, params: { lng }, @@ -146,6 +147,7 @@ export default async function Login({ )}
+ {isSupabaseConnected && } + {/* 如果用户没有登录会出现谷歌的sign in按钮登录之后不会出现 */} {!user && }
diff --git a/app/api/oauth/callback/route.ts b/app/api/oauth/callback/route.ts new file mode 100644 index 0000000..693246c --- /dev/null +++ b/app/api/oauth/callback/route.ts @@ -0,0 +1,106 @@ +// api/oauth/callback.js +import { createClient } from "@/utils/supabase/server"; +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import axios from "axios"; +//supabase +import { insertUserProfile } from "@/utils/supabase/supabaseutils"; +import { setVip } from "@/utils/supabase/serverutils"; +export async function GET(request: Request) { + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get("code"); + if (code) { + const cookieStore = cookies(); + const supabase = createClient(cookieStore); + // await supabase.auth.exchangeCodeForSession(code); + + // 使用授权码请求访问令牌 + const tokenResponse = await getToken(code); + + const accessToken = tokenResponse!.data.access_token; + console.log("accessToekn", accessToken); + // 使用访问令牌获取用户信息 + const userResponse = await axios.get("https://connect.linux.do/api/user", { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + const userInfo = userResponse.data; + const uuid = "9e1c30b5-723c-4805-b3b8-0ac3c1923514"; //生成密码 + let userId = null; + // 尝试注册新用户 + const signUpResponse = await supabase.auth.signUp({ + email: `${userInfo.username}@linux.do`, // 使用模板字符串构建email + password: uuid, // 使用uuid作为密码 + }); + + if (signUpResponse.error) { + // 如果用户已存在,尝试登录来获取用户信息 + await supabase.auth.signOut(); + const signInResponse = await supabase.auth.signInWithPassword({ + email: `${userInfo.username}@linux.do`, + password: uuid, + }); + if (signInResponse.error) { + console.error("Error logging in existing user:", signInResponse.error); + // 处理登录失败的情况 + } else { + //signin成功 + userId = signInResponse.data.user!.id; + } + } else { + //signup成功之后可能要signin一次 + const signInResponse = await supabase.auth.signInWithPassword({ + email: `${userInfo.username}@linux.do`, + password: uuid, + }); + console.log("signInResponse:", signInResponse); + userId = signUpResponse.data.user!.id; + } + // 如果获取到了用户ID,进行后续操作 + if (userId) { + // console.log("signUpResponse.data:", signUpResponse.data); + //插入信息并设置VIP + await insertUserProfile(signUpResponse.data, supabase); + await setVip(supabase, userId, true, "Linuxdo"); + } else { + return new Response( + JSON.stringify({ error: "Unable to register or login the user" }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + // URL to redirect to after sign in process completes + return NextResponse.redirect(requestUrl.origin); + } + + async function getToken(code: string) { + // 使用client_id和client_secret创建Basic Auth凭证 + try { + const tokenResponse = await axios.post( + "https://connect.linux.do/oauth2/token", + `grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent( + process.env.REDIRECT_URI + )}`, + { + headers: { + Authorization: `Basic ${Buffer.from( + `${process.env.CLIENT_ID}:${process.env.CLIENT_SECRET}` + ).toString("base64")}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + } + ); + + // 处理tokenResponse... + return tokenResponse; + } catch (error) { + // 处理错误... + console.error(error); + } + } +} diff --git a/components/LinuxdoSignin.tsx b/components/LinuxdoSignin.tsx new file mode 100644 index 0000000..66599a1 --- /dev/null +++ b/components/LinuxdoSignin.tsx @@ -0,0 +1,28 @@ +"use client"; +import React from "react"; + +const LinuxdoSignin = () => { + const handleLogin = () => { + // 构建授权URL + const clientId = "UrgIEI0n03tveTmaOV0IU8qRY4DttGY4"; + const responseType = "code"; + const authUrl = `https://connect.linux.do/oauth2/authorize?response_type=${responseType}&client_id=${clientId}&state=ttt1`; + + // 重定向到授权页面 + window.location.href = authUrl; + }; + + return ( +
+ +
+ ); +}; + +export default LinuxdoSignin; diff --git a/middleware.ts b/middleware.ts index 96936e2..2843cd3 100644 --- a/middleware.ts +++ b/middleware.ts @@ -69,6 +69,6 @@ export const config = { * - favicon.ico (favicon file) * Feel free to modify this pattern to include more paths. */ - "/((?!_next/static|_next/image|favicon.ico|twitter-image.png|opengraph-image.png|manifest.json|site.webmanifest|favicon-32x32.png|favicon-16x16.png|apple-touch-icon.png|android-chrome-512x512.png|android-chrome-192x192.png|service-worker.js|serviceregister.js|global.css|sitemap.xml|robots.txt).*)", + "/((?!_next/static|_next/image|favicon.ico|twitter-image.png|opengraph-image.png|manifest.json|site.webmanifest|favicon-32x32.png|favicon-16x16.png|apple-touch-icon.png|android-chrome-512x512.png|android-chrome-192x192.png|service-worker.js|serviceregister.js|global.css|sitemap.xml|robots.txt|api/oauth/callback).*)", ], }; diff --git a/utils/supabase/serverutils.ts b/utils/supabase/serverutils.ts new file mode 100644 index 0000000..29fa989 --- /dev/null +++ b/utils/supabase/serverutils.ts @@ -0,0 +1,47 @@ +import { NextResponse } from "next/server"; + +import { SupabaseClient } from "@supabase/supabase-js"; +export async function setVip( + supabaseAdmin: SupabaseClient, + userId: string, + isVip = true, + source = "Linuxdo", + startDate = new Date(), + endDate = new Date() +) { + if (!userId) + return NextResponse.json({ message: "No user found" }, { status: 403 }); + const { data, error } = await supabaseAdmin.from("vip_statuses").upsert( + { + user_id: userId, + is_vip: isVip, + source: source, + start_date: startDate, + end_date: endDate, + }, + { onConflict: "user_id" } + ); + if (error) { + console.error("设置 VIP 失败:", error); + return NextResponse.json( + { message: "Failed to set VIP 设置 VIP 状态失败" }, + { status: 403 } + ); + } + return NextResponse.json({ message: "Success VIP 状态已更新:" }); +} + +async function getUserId(supabaseAdmin: SupabaseClient, email: string) { + const { data, error } = await supabaseAdmin + .from("profiles") + .select("id") + .eq("email", email) + .single(); + + if (error) { + console.error("查询用户 ID 失败:", error); + return null; + } + + return data.id; +} diff --git a/utils/supabase/supabaseutils.ts b/utils/supabase/supabaseutils.ts index 1f40756..36669db 100644 --- a/utils/supabase/supabaseutils.ts +++ b/utils/supabase/supabaseutils.ts @@ -186,6 +186,7 @@ export async function insertUserProfile(data: any, supabase: SupabaseClient) { } if (user) { + // console.log("user in insertUserProfile:", user); const currentTime = new Date().toISOString(); // 生成ISO格式的时间字符串 const { data, error: profileError } = await supabase