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