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
|
#node转发设置为 /api/v1/chat/completions https://one.caifree.com sk-aiHrrRLYUUelHstX69E9484509254dBf92061d6744FfFaD1
|
||||||
VERCEL_URL=https://www.paperai.life
|
VERCEL_URL=https://www.paperai.life
|
||||||
NODE_ENV=development
|
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
|
NEXT_PUBLIC_PUBMED_API_KEY=057616e7ce6c722f2ae8679e38a8be9b1a09
|
||||||
VERCEL_URL=https://www.paperai.life
|
VERCEL_URL=https://www.paperai.life
|
||||||
NODE_ENV=production
|
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";
|
import { insertUserProfile } from "@/utils/supabase/supabaseutils";
|
||||||
// SignInWithProvider
|
// SignInWithProvider
|
||||||
import { SignInWithProvider } from "@/components/SignInWithProvider";
|
import { SignInWithProvider } from "@/components/SignInWithProvider";
|
||||||
|
import LinuxdoSignin from "@/components/LinuxdoSignin";
|
||||||
export default async function Login({
|
export default async function Login({
|
||||||
searchParams,
|
searchParams,
|
||||||
params: { lng },
|
params: { lng },
|
||||||
|
@ -146,6 +147,7 @@ export default async function Login({
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
<div>
|
<div>
|
||||||
|
<LinuxdoSignin />
|
||||||
<SignInWithProvider
|
<SignInWithProvider
|
||||||
provider="github"
|
provider="github"
|
||||||
redirectTo="https://www.paperai.life/welcome"
|
redirectTo="https://www.paperai.life/welcome"
|
||||||
|
|
|
@ -48,6 +48,7 @@ export default async function Index({ params: { lng } }: IndexProps) {
|
||||||
{/* 用来表示是否显示论文列表页 */}
|
{/* 用来表示是否显示论文列表页 */}
|
||||||
<PaperListButtonWrapper />
|
<PaperListButtonWrapper />
|
||||||
{isSupabaseConnected && <AuthButton />}
|
{isSupabaseConnected && <AuthButton />}
|
||||||
|
{/* 如果用户没有登录会出现谷歌的sign in按钮登录之后不会出现 */}
|
||||||
{!user && <GoogleSignIn />}
|
{!user && <GoogleSignIn />}
|
||||||
<SettingsLink />
|
<SettingsLink />
|
||||||
</div>
|
</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)
|
* - favicon.ico (favicon file)
|
||||||
* Feel free to modify this pattern to include more paths.
|
* 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) {
|
if (user) {
|
||||||
|
// console.log("user in insertUserProfile:", user);
|
||||||
const currentTime = new Date().toISOString(); // 生成ISO格式的时间字符串
|
const currentTime = new Date().toISOString(); // 生成ISO格式的时间字符串
|
||||||
|
|
||||||
const { data, error: profileError } = await supabase
|
const { data, error: profileError } = await supabase
|
||||||
|
|
Loading…
Reference in New Issue
Block a user