feat: 完成linuxdo oauth
This commit is contained in:
parent
8730415352
commit
0090ffd3bb
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -48,6 +48,7 @@ export default async function Index({ params: { lng } }: IndexProps) {
|
|||
{/* 用来表示是否显示论文列表页 */}
|
||||
<PaperListButtonWrapper />
|
||||
{isSupabaseConnected && <AuthButton />}
|
||||
{/* 如果用户没有登录会出现谷歌的sign in按钮登录之后不会出现 */}
|
||||
{!user && <GoogleSignIn />}
|
||||
<SettingsLink />
|
||||
</div>
|
||||
|
|
106
app/api/oauth/callback/route.ts
Normal file
106
app/api/oauth/callback/route.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
28
components/LinuxdoSignin.tsx
Normal file
28
components/LinuxdoSignin.tsx
Normal 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;
|
|
@ -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).*)",
|
||||
],
|
||||
};
|
||||
|
|
47
utils/supabase/serverutils.ts
Normal file
47
utils/supabase/serverutils.ts
Normal 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;
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user