183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
import { useEffect } from "react";
|
||
import { useSearchParams, Form } from "@remix-run/react";
|
||
import { type MetaFunction, type LoaderFunctionArgs, type ActionFunctionArgs, redirect } from "@remix-run/node";
|
||
import { OAuthClient } from "~/api/login/oauth-client";
|
||
import { OAUTH_CONFIG } from "~/config/api-config";
|
||
import { getUserSession, getSession, createUserSession } from "~/api/login/auth.server";
|
||
import styles from "~/styles/pages/login.css?url";
|
||
|
||
export const links = () => [
|
||
{ rel: "stylesheet", href: styles }
|
||
];
|
||
|
||
export const meta: MetaFunction = () => {
|
||
return [
|
||
{ title: "中国烟草AI合同及卷宗审核系统 - 登录" },
|
||
{ name: "description", content: "中国烟草AI合同及卷宗审核系统登录页面" },
|
||
];
|
||
};
|
||
|
||
// 加载器,获取当前会话状态
|
||
export async function loader({ request }: LoaderFunctionArgs) {
|
||
const { isAuthenticated } = await getUserSession(request);
|
||
|
||
// 如果已登录,重定向到首页
|
||
if (isAuthenticated) {
|
||
return redirect("/");
|
||
}
|
||
|
||
// 获取重定向URL并保存到session
|
||
const url = new URL(request.url);
|
||
const redirectTo = url.searchParams.get("redirect") || "/";
|
||
|
||
const session = await getSession(request);
|
||
session.set("redirectTo", redirectTo);
|
||
|
||
return Response.json({
|
||
isAuthenticated: false,
|
||
redirectTo
|
||
});
|
||
}
|
||
|
||
// 处理表单提交的action函数
|
||
export async function action({ request }: ActionFunctionArgs) {
|
||
const formData = await request.formData();
|
||
const intent = formData.get("intent");
|
||
|
||
if (intent === "temp_admin_login") {
|
||
// 获取重定向目标
|
||
const session = await getSession(request);
|
||
const redirectTo = session.get("redirectTo") || "/";
|
||
|
||
// 创建管理员会话
|
||
return createUserSession(true, 'developer', redirectTo);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
export default function Login() {
|
||
const [searchParams] = useSearchParams();
|
||
const error = searchParams.get("error");
|
||
|
||
// 获取错误消息的友好描述
|
||
const getErrorMessage = (error: string | null) => {
|
||
if (!error) return null;
|
||
|
||
switch (error) {
|
||
case "missing_code":
|
||
return "登录过程中缺少授权码,请重新登录";
|
||
case "invalid_state":
|
||
return "登录状态验证失败,请重新登录";
|
||
case "token_error":
|
||
return "获取访问令牌失败,请重新登录";
|
||
case "userinfo_error":
|
||
return "获取用户信息失败,请重新登录";
|
||
case "callback_error":
|
||
return "登录回调处理失败,请重新登录";
|
||
default:
|
||
return decodeURIComponent(error);
|
||
}
|
||
};
|
||
|
||
// 处理OAuth2.0登录
|
||
const handleOAuthLogin = () => {
|
||
try {
|
||
// 创建OAuth客户端
|
||
const oauthClient = new OAuthClient(OAUTH_CONFIG);
|
||
|
||
// 生成状态值
|
||
const state = oauthClient.generateState();
|
||
|
||
// 将状态值保存到localStorage(用于后续验证)
|
||
localStorage.setItem("oauth_state", state);
|
||
|
||
// 获取授权URL
|
||
const authorizeUrl = oauthClient.getAuthorizeUrl(state);
|
||
|
||
// 重定向到IDaaS登录页面
|
||
window.location.href = authorizeUrl;
|
||
} catch (error) {
|
||
console.error("启动OAuth2.0登录失败:", error);
|
||
alert("登录系统初始化失败,请联系系统管理员");
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
// 检查OAuth配置是否完整
|
||
if (!OAUTH_CONFIG.serverUrl || !OAUTH_CONFIG.clientId || !OAUTH_CONFIG.clientSecret) {
|
||
console.error("OAuth2.0配置不完整:", OAUTH_CONFIG);
|
||
}
|
||
}, []);
|
||
|
||
return (
|
||
<div className="login-page">
|
||
<div className="login-container">
|
||
<div className="login-header">
|
||
<h1 className="login-title">中国烟草AI合同及卷宗审核系统</h1>
|
||
</div>
|
||
|
||
<div className="login-form-container">
|
||
<h2 className="login-subtitle">统一身份认证登录</h2>
|
||
|
||
{error && (
|
||
<div className="error-message-container">
|
||
<div className="error-icon"><i className="ri-error-warning-line"></i></div>
|
||
<div className="error-text">{getErrorMessage(error)}</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="oauth-login-section">
|
||
<div className="login-description">
|
||
<p>请点击下方按钮进行统一身份认证登录</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleOAuthLogin}
|
||
className="oauth-login-button"
|
||
type="button"
|
||
>
|
||
<i className="ri-shield-user-line"></i>
|
||
统一身份认证登录
|
||
</button>
|
||
|
||
<div className="login-tips">
|
||
<p>
|
||
<i className="ri-information-line"></i>
|
||
系统将跳转到统一身份认证平台进行登录
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 临时管理员登录区域 */}
|
||
<div className="temp-login-section">
|
||
<div className="section-divider">
|
||
<span>或</span>
|
||
</div>
|
||
|
||
<Form method="post" className="temp-login-form">
|
||
<input type="hidden" name="intent" value="temp_admin_login" />
|
||
<button
|
||
type="submit"
|
||
className="temp-admin-login-button"
|
||
>
|
||
<i className="ri-admin-line"></i>
|
||
临时管理员登录
|
||
</button>
|
||
<div className="temp-login-tips">
|
||
<p>
|
||
<i className="ri-alert-line"></i>
|
||
仅供开发测试使用,将以管理员身份登录
|
||
</p>
|
||
</div>
|
||
</Form>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="login-footer">
|
||
<p>© 2025 中国烟草 版权所有</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|