feat: 完成linuxdo oauth

This commit is contained in:
14790897 2024-03-13 20:51:58 +08:00
parent 8730415352
commit 0090ffd3bb
9 changed files with 193 additions and 2 deletions

View File

@ -13,3 +13,6 @@ NEXT_PUBLIC_PAPER_URL=/api/paper
#node转发设置为 /api/v1/chat/completions https://one.caifree.com sk-aiHrrRLYUUelHstX69E9484509254dBf92061d6744FfFaD1
VERCEL_URL=https://www.paperai.life
NODE_ENV=development
CLIENT_ID=UrgIEI0n03tveTmaOV0IU8qRY4DttGY4
CLIENT_SECRET=ljShbIlIrfULu4BTUVTT4azeR90PtAif
REDIRECT_URI=http://localhost:3000/api/oauth/callback

View File

@ -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

View File

@ -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({
)}
</form>
<div>
<LinuxdoSignin />
<SignInWithProvider
provider="github"
redirectTo="https://www.paperai.life/welcome"

View File

@ -48,6 +48,7 @@ export default async function Index({ params: { lng } }: IndexProps) {
{/* 用来表示是否显示论文列表页 */}
<PaperListButtonWrapper />
{isSupabaseConnected && <AuthButton />}
{/* 如果用户没有登录会出现谷歌的sign in按钮登录之后不会出现 */}
{!user && <GoogleSignIn />}
<SettingsLink />
</div>

View File

@ -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);
}
}
}

View File

@ -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 (
<div>
<button
onClick={handleLogin}
className="bg-gradient-to-r from-yellow-400 to-yellow-500 text-white rounded-md px-4 py-2 mb-2 flex items-center justify-center gap-2 hover:from-yellow-500 hover:to-yellow-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-yellow-400 shadow-lg hover:shadow-xl transition ease-in duration-200 w-full
"
>
Login with Linuxdo(free VIP)
</button>
</div>
);
};
export default LinuxdoSignin;

View File

@ -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).*)",
],
};

View File

@ -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;
}

View File

@ -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