移除Host头验证中间件及相关逻辑,简化OAuth相关API的请求处理,优化代码结构和可读性。
This commit is contained in:
@@ -171,11 +171,15 @@ export async function uploadContractTemplate(
|
|||||||
const uploadUrl = `${UPLOAD_URL}/upload_contract_template`;
|
const uploadUrl = `${UPLOAD_URL}/upload_contract_template`;
|
||||||
console.log('【合同模板上传】准备发送请求到服务器:', uploadUrl);
|
console.log('【合同模板上传】准备发送请求到服务器:', uploadUrl);
|
||||||
|
|
||||||
// 设置请求头(按需去掉JWT认证)
|
// 设置请求头
|
||||||
const headers: HeadersInit = {
|
const headers: HeadersInit = {
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (jwtToken) {
|
||||||
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
const response = await fetch(uploadUrl, {
|
const response = await fetch(uploadUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -251,11 +255,15 @@ export async function appendContractAttachments(
|
|||||||
const uploadUrl = `${UPLOAD_URL}/contracts/${documentId}/append_attachments`;
|
const uploadUrl = `${UPLOAD_URL}/contracts/${documentId}/append_attachments`;
|
||||||
console.log('【合同附件追加】准备发送请求到服务器:', uploadUrl);
|
console.log('【合同附件追加】准备发送请求到服务器:', uploadUrl);
|
||||||
|
|
||||||
// 设置请求头(根据需求,附件追加不做JWT认证)
|
// 设置请求头
|
||||||
const headers: HeadersInit = {
|
const headers: HeadersInit = {
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (jwtToken) {
|
||||||
|
headers['Authorization'] = `Bearer ${jwtToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
const response = await fetch(uploadUrl, {
|
const response = await fetch(uploadUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -349,16 +357,12 @@ export async function uploadDocumentToServer(
|
|||||||
// const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, {
|
// const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, {
|
||||||
try {
|
try {
|
||||||
// console.log('【调试】开始fetch请求...');
|
// console.log('【调试】开始fetch请求...');
|
||||||
const headers: HeadersInit = {
|
|
||||||
'X-File-Name': encodeURIComponent(fileName),
|
|
||||||
'Accept': 'application/json'
|
|
||||||
};
|
|
||||||
if (jwtToken) {
|
|
||||||
headers['Authorization'] = `Bearer ${jwtToken}`;
|
|
||||||
}
|
|
||||||
const response = await fetch(uploadUrl, {
|
const response = await fetch(uploadUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers: {
|
||||||
|
'X-File-Name': encodeURIComponent(fileName),
|
||||||
|
'Authorization': `Bearer ${jwtToken || ''}`
|
||||||
|
},
|
||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -736,18 +736,6 @@ export async function simpleRootLogin(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 密码强度验证
|
|
||||||
// const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
|
|
||||||
// if (!passwordRegex.test(password.trim())) {
|
|
||||||
// return new Response(JSON.stringify({
|
|
||||||
// success: false,
|
|
||||||
// error: "密码必须至少8位,包含大小写字母和数字"
|
|
||||||
// }), {
|
|
||||||
// status: 400,
|
|
||||||
// headers: { "Content-Type": "application/json" }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 调用登录接口
|
// 调用登录接口
|
||||||
const loginResponse = await fetch(`${API_BASE_URL}/password_login`, {
|
const loginResponse = await fetch(`${API_BASE_URL}/password_login`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -340,10 +340,7 @@ const getCurrentConfig = (): ApiConfig => {
|
|||||||
return getConfigFromEnv(defaultConfig);
|
return getConfigFromEnv(defaultConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调试信息(仅在开发环境显示)
|
console.log('🔧 最终配置:', defaultConfig);
|
||||||
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
|
|
||||||
console.log('🔧 最终配置:', defaultConfig);
|
|
||||||
}
|
|
||||||
return defaultConfig;
|
return defaultConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,238 +0,0 @@
|
|||||||
/**
|
|
||||||
* Host头验证中间件
|
|
||||||
* 用于防止Host Header注入攻击
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface HostValidationResult {
|
|
||||||
valid: boolean;
|
|
||||||
error?: string;
|
|
||||||
allowedHost?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取允许的Host列表
|
|
||||||
* 根据不同环境和端口配置返回相应的Host白名单
|
|
||||||
*/
|
|
||||||
function getAllowedHosts(): string[] {
|
|
||||||
// 基础IP地址
|
|
||||||
const baseIP = '10.79.97.17';
|
|
||||||
const testIP = '172.16.0.55';
|
|
||||||
const localhostIP = '127.0.0.1';
|
|
||||||
|
|
||||||
// 生产环境端口列表
|
|
||||||
const productionPorts = ['51703', '51704', '51705', '51706', '51707', '51708'];
|
|
||||||
|
|
||||||
// 测试环境端口列表
|
|
||||||
const testPorts = ['5173', '5174', '5175', '5176', '5177', '5178'];
|
|
||||||
|
|
||||||
const allowedHosts: string[] = [];
|
|
||||||
|
|
||||||
// 添加基础IP(不带端口)
|
|
||||||
allowedHosts.push(baseIP, testIP, 'localhost', localhostIP);
|
|
||||||
|
|
||||||
// 添加生产环境的IP:端口组合
|
|
||||||
productionPorts.forEach(port => {
|
|
||||||
allowedHosts.push(`${baseIP}:${port}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加测试环境的IP:端口组合
|
|
||||||
testPorts.forEach(port => {
|
|
||||||
allowedHosts.push(`${testIP}:${port}`);
|
|
||||||
allowedHosts.push(`localhost:${port}`);
|
|
||||||
allowedHosts.push(`${localhostIP}:${port}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 开发环境额外允许的Host
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
allowedHosts.push('localhost:3000', '127.0.0.1:3000');
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowedHosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证Host头是否在允许列表中
|
|
||||||
* @param request - Remix Request对象
|
|
||||||
* @returns 验证结果
|
|
||||||
*/
|
|
||||||
export function validateHost(request: Request): HostValidationResult {
|
|
||||||
const host = request.headers.get('host');
|
|
||||||
const referer = request.headers.get('referer');
|
|
||||||
const userAgent = request.headers.get('user-agent');
|
|
||||||
|
|
||||||
// 获取允许的Host列表
|
|
||||||
const allowedHosts = getAllowedHosts();
|
|
||||||
|
|
||||||
console.log('🔒 Host验证开始:', {
|
|
||||||
host: host,
|
|
||||||
referer: referer,
|
|
||||||
userAgent: userAgent ? userAgent.substring(0, 50) + '...' : null,
|
|
||||||
allowedHosts: allowedHosts
|
|
||||||
});
|
|
||||||
|
|
||||||
// 1. 验证Host头是否存在
|
|
||||||
if (!host) {
|
|
||||||
console.error('❌ Host头缺失');
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: 'Missing Host header'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 验证Host头是否在允许列表中
|
|
||||||
if (!allowedHosts.includes(host)) {
|
|
||||||
console.error('❌ Host头不在允许列表中:', {
|
|
||||||
host: host,
|
|
||||||
allowedHosts: allowedHosts
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: `Invalid Host header: ${host}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 验证Referer头(如果存在)
|
|
||||||
if (referer) {
|
|
||||||
try {
|
|
||||||
const refererUrl = new URL(referer);
|
|
||||||
const refererHost = refererUrl.host;
|
|
||||||
|
|
||||||
if (!allowedHosts.includes(refererHost)) {
|
|
||||||
console.error('❌ Referer头不在允许列表中:', {
|
|
||||||
referer: referer,
|
|
||||||
refererHost: refererHost,
|
|
||||||
allowedHosts: allowedHosts
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: `Invalid Referer header: ${refererHost}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Referer头格式无效:', referer);
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: `Malformed Referer header: ${referer}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Host验证通过:', host);
|
|
||||||
return {
|
|
||||||
valid: true,
|
|
||||||
allowedHost: host
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证Origin头是否在允许列表中
|
|
||||||
* @param request - Remix Request对象
|
|
||||||
* @returns 验证结果
|
|
||||||
*/
|
|
||||||
export function validateOrigin(request: Request): HostValidationResult {
|
|
||||||
const origin = request.headers.get('origin');
|
|
||||||
|
|
||||||
if (!origin) {
|
|
||||||
// Origin头不是必须的,某些请求(如直接访问)可能没有Origin头
|
|
||||||
return { valid: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const originUrl = new URL(origin);
|
|
||||||
const originHost = originUrl.host;
|
|
||||||
const allowedHosts = getAllowedHosts();
|
|
||||||
|
|
||||||
if (!allowedHosts.includes(originHost)) {
|
|
||||||
console.error('❌ Origin头不在允许列表中:', {
|
|
||||||
origin: origin,
|
|
||||||
originHost: originHost,
|
|
||||||
allowedHosts: allowedHosts
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: `Invalid Origin header: ${originHost}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Origin验证通过:', origin);
|
|
||||||
return { valid: true };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Origin头格式无效:', origin);
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: `Malformed Origin header: ${origin}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 完整的请求验证
|
|
||||||
* 包括Host、Referer、Origin头的验证
|
|
||||||
* @param request - Remix Request对象
|
|
||||||
* @returns 验证结果
|
|
||||||
*/
|
|
||||||
export function validateRequest(request: Request): HostValidationResult {
|
|
||||||
// 1. 验证Host头
|
|
||||||
const hostValidation = validateHost(request);
|
|
||||||
if (!hostValidation.valid) {
|
|
||||||
return hostValidation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 验证Origin头
|
|
||||||
const originValidation = validateOrigin(request);
|
|
||||||
if (!originValidation.valid) {
|
|
||||||
return originValidation;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { valid: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否为受保护的路由
|
|
||||||
* 某些路由可能需要更严格的验证
|
|
||||||
* @param pathname - 请求路径
|
|
||||||
* @returns 是否为受保护路由
|
|
||||||
*/
|
|
||||||
export function isProtectedRoute(pathname: string): boolean {
|
|
||||||
const protectedRoutes = [
|
|
||||||
'/callback',
|
|
||||||
'/api/oauth/token',
|
|
||||||
'/api/oauth/userinfo',
|
|
||||||
'/logout',
|
|
||||||
'/admin'
|
|
||||||
];
|
|
||||||
|
|
||||||
return protectedRoutes.some(route => pathname.startsWith(route));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录安全事件
|
|
||||||
* @param event - 事件类型
|
|
||||||
* @param details - 事件详情
|
|
||||||
* @param request - 请求对象
|
|
||||||
*/
|
|
||||||
export function logSecurityEvent(
|
|
||||||
event: 'host_validation_failed' | 'origin_validation_failed' | 'referer_validation_failed',
|
|
||||||
details: string,
|
|
||||||
request: Request
|
|
||||||
) {
|
|
||||||
const timestamp = new Date().toISOString();
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const clientIP = request.headers.get('x-forwarded-for') ||
|
|
||||||
request.headers.get('x-real-ip') ||
|
|
||||||
'unknown';
|
|
||||||
|
|
||||||
console.error(`🚨 安全事件: ${event}`, {
|
|
||||||
timestamp: timestamp,
|
|
||||||
url: url.pathname + url.search,
|
|
||||||
host: request.headers.get('host'),
|
|
||||||
referer: request.headers.get('referer'),
|
|
||||||
origin: request.headers.get('origin'),
|
|
||||||
userAgent: request.headers.get('user-agent'),
|
|
||||||
clientIP: clientIP,
|
|
||||||
details: details
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: 这里可以添加更复杂的日志记录逻辑
|
|
||||||
// 比如写入数据库、发送告警邮件等
|
|
||||||
}
|
|
||||||
@@ -38,11 +38,6 @@ import {
|
|||||||
logout,
|
logout,
|
||||||
type UserRole
|
type UserRole
|
||||||
} from "~/api/login/auth.server";
|
} from "~/api/login/auth.server";
|
||||||
import {
|
|
||||||
validateRequest,
|
|
||||||
isProtectedRoute,
|
|
||||||
logSecurityEvent
|
|
||||||
} from "~/middleware/host-validation";
|
|
||||||
|
|
||||||
// 定义需要高级权限的路径
|
// 定义需要高级权限的路径
|
||||||
export const developerOnlyPaths = [
|
export const developerOnlyPaths = [
|
||||||
@@ -75,32 +70,6 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const pathname = url.pathname;
|
const pathname = url.pathname;
|
||||||
|
|
||||||
// ==================== Host头验证 ====================
|
|
||||||
// 1. 首先进行Host头验证,防止Host Header注入攻击
|
|
||||||
const hostValidation = validateRequest(request);
|
|
||||||
if (!hostValidation.valid) {
|
|
||||||
// 记录安全事件
|
|
||||||
logSecurityEvent('host_validation_failed', hostValidation.error || 'Unknown validation error', request);
|
|
||||||
|
|
||||||
// 对于受保护的路由,直接返回403错误
|
|
||||||
if (isProtectedRoute(pathname)) {
|
|
||||||
throw new Response("Forbidden: Invalid Host header", {
|
|
||||||
status: 403,
|
|
||||||
statusText: "Forbidden"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于普通路由,重定向到错误页面
|
|
||||||
console.error('❌ Host验证失败:', hostValidation.error);
|
|
||||||
throw new Response("Forbidden: Invalid request headers", {
|
|
||||||
status: 403,
|
|
||||||
statusText: "Forbidden"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('✅ Host验证通过,继续处理请求');
|
|
||||||
// ==================== Host头验证结束 ====================
|
|
||||||
|
|
||||||
// 排除不需要登录验证的路径
|
// 排除不需要登录验证的路径
|
||||||
const publicPaths = ['/login', '/favicon.ico', '/callback'];
|
const publicPaths = ['/login', '/favicon.ico', '/callback'];
|
||||||
const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
|
const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { type ActionFunctionArgs, json } from "@remix-run/node";
|
import { type ActionFunctionArgs, json } from "@remix-run/node";
|
||||||
import { OAuthClient } from "~/api/login/oauth-client";
|
import { OAuthClient } from "~/api/login/oauth-client";
|
||||||
import { OAUTH_CONFIG } from "~/config/api-config";
|
import { OAUTH_CONFIG } from "~/config/api-config";
|
||||||
import { validateRequest, logSecurityEvent } from "~/middleware/host-validation";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这个Action作为获取OAuth Access Token的服务器端代理。
|
* 这个Action作为获取OAuth Access Token的服务器端代理。
|
||||||
@@ -9,15 +8,7 @@ import { validateRequest, logSecurityEvent } from "~/middleware/host-validation"
|
|||||||
* 以避免在网络策略限制服务器直接访问外部服务时出现问题。
|
* 以避免在网络策略限制服务器直接访问外部服务时出现问题。
|
||||||
*/
|
*/
|
||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
// 1. Host头验证
|
// 1. 只允许POST请求
|
||||||
const hostValidation = validateRequest(request);
|
|
||||||
if (!hostValidation.valid) {
|
|
||||||
logSecurityEvent('host_validation_failed', hostValidation.error || 'Unknown validation error', request);
|
|
||||||
console.error('❌ OAuth Token API Host验证失败:', hostValidation.error);
|
|
||||||
return json({ success: false, error: "Forbidden: Invalid Host header" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 只允许POST请求
|
|
||||||
if (request.method !== "POST") {
|
if (request.method !== "POST") {
|
||||||
return json({ success: false, error: "Method Not Allowed" }, { status: 405 });
|
return json({ success: false, error: "Method Not Allowed" }, { status: 405 });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,12 @@
|
|||||||
import { type ActionFunctionArgs, json } from "@remix-run/node";
|
import { type ActionFunctionArgs, json } from "@remix-run/node";
|
||||||
import { OAuthClient } from "~/api/login/oauth-client";
|
import { OAuthClient } from "~/api/login/oauth-client";
|
||||||
import { OAUTH_CONFIG } from "~/config/api-config";
|
import { OAUTH_CONFIG } from "~/config/api-config";
|
||||||
import { validateRequest, logSecurityEvent } from "~/middleware/host-validation";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这个Action作为获取用户信息的服务器端代理。
|
* 这个Action作为获取用户信息的服务器端代理。
|
||||||
* 它接收来自前端的`access_token`,然后在后端安全地获取用户信息。
|
* 它接收来自前端的`access_token`,然后在后端安全地获取用户信息。
|
||||||
*/
|
*/
|
||||||
export async function action({ request }: ActionFunctionArgs) {
|
export async function action({ request }: ActionFunctionArgs) {
|
||||||
// 1. Host头验证
|
|
||||||
const hostValidation = validateRequest(request);
|
|
||||||
if (!hostValidation.valid) {
|
|
||||||
logSecurityEvent('host_validation_failed', hostValidation.error || 'Unknown validation error', request);
|
|
||||||
console.error('❌ OAuth UserInfo API Host验证失败:', hostValidation.error);
|
|
||||||
return json({ success: false, error: "Forbidden: Invalid Host header" }, { status: 403 });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 只允许POST请求
|
|
||||||
if (request.method !== "POST") {
|
if (request.method !== "POST") {
|
||||||
return json({ success: false, error: "Method Not Allowed" }, { status: 405 });
|
return json({ success: false, error: "Method Not Allowed" }, { status: 405 });
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-17
@@ -1,23 +1,8 @@
|
|||||||
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
|
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
|
||||||
import { createUserSession, saveUserInfo, type UserRole } from "~/api/login/auth.server";
|
import { createUserSession, saveUserInfo } from "~/api/login/auth.server";
|
||||||
import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt";
|
import { JWTUtils, type UserInfoForJWT } from "~/utils/jwt";
|
||||||
import { validateRequest, logSecurityEvent } from "~/middleware/host-validation";
|
|
||||||
|
|
||||||
export async function loader({ request }: LoaderFunctionArgs) {
|
export async function loader({ request }: LoaderFunctionArgs) {
|
||||||
// ==================== Host头验证 ====================
|
|
||||||
// OAuth回调是安全敏感的操作,需要严格验证请求来源
|
|
||||||
const hostValidation = validateRequest(request);
|
|
||||||
if (!hostValidation.valid) {
|
|
||||||
// 记录安全事件
|
|
||||||
logSecurityEvent('host_validation_failed', hostValidation.error || 'Unknown validation error', request);
|
|
||||||
|
|
||||||
console.error('❌ OAuth回调Host验证失败:', hostValidation.error);
|
|
||||||
return redirect("/login?error=invalid_host");
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('✅ OAuth回调Host验证通过');
|
|
||||||
// ==================== Host头验证结束 ====================
|
|
||||||
|
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703")
|
const origin = url.origin; // 获取请求的源 (e.g., "http://10.79.97.17:51703")
|
||||||
const code = url.searchParams.get("code");
|
const code = url.searchParams.get("code");
|
||||||
@@ -158,7 +143,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
|||||||
// 使用统一的session创建函数
|
// 使用统一的session创建函数
|
||||||
return createUserSession({
|
return createUserSession({
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
userRole: userRole as UserRole,
|
userRole: userRole as 'common' | 'developer',
|
||||||
redirectTo,
|
redirectTo,
|
||||||
accessToken: tokenResponse.access_token,
|
accessToken: tokenResponse.access_token,
|
||||||
refreshToken: tokenResponse.refresh_token,
|
refreshToken: tokenResponse.refresh_token,
|
||||||
|
|||||||
@@ -788,14 +788,13 @@ export default function DocumentsIndex() {
|
|||||||
try {
|
try {
|
||||||
setAttachmentUploading(true);
|
setAttachmentUploading(true);
|
||||||
|
|
||||||
const jwtToken = (loaderData.frontendJWT as string | undefined) || (loaderData.userInfo?.frontend_jwt as unknown as string | undefined);
|
|
||||||
const result = await appendContractAttachments(
|
const result = await appendContractAttachments(
|
||||||
selectedDocumentId,
|
selectedDocumentId,
|
||||||
attachmentFiles,
|
attachmentFiles,
|
||||||
attachmentMergeMode,
|
attachmentMergeMode,
|
||||||
true, // isReprocess
|
true, // isReprocess
|
||||||
attachmentRemark || undefined,
|
attachmentRemark || undefined,
|
||||||
jwtToken
|
loaderData.frontendJWT as string | undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
@@ -863,12 +862,11 @@ export default function DocumentsIndex() {
|
|||||||
try {
|
try {
|
||||||
setTemplateUploading(true);
|
setTemplateUploading(true);
|
||||||
|
|
||||||
const jwtToken = (loaderData.frontendJWT as string | undefined) || (loaderData.userInfo?.frontend_jwt as unknown as string | undefined);
|
|
||||||
const result = await uploadContractTemplate(
|
const result = await uploadContractTemplate(
|
||||||
templateFile,
|
templateFile,
|
||||||
selectedDocumentId,
|
selectedDocumentId,
|
||||||
undefined, // comparisonId
|
undefined, // comparisonId
|
||||||
jwtToken
|
loaderData.frontendJWT as string | undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
|
|||||||
@@ -512,14 +512,13 @@ export default function RulesFiles() {
|
|||||||
try {
|
try {
|
||||||
setAttachmentUploading(true);
|
setAttachmentUploading(true);
|
||||||
const docId = parseInt(selectedDocumentId, 10);
|
const docId = parseInt(selectedDocumentId, 10);
|
||||||
const jwtToken = (frontendJWT as string | undefined) || (userInfo?.frontend_jwt as unknown as string | undefined);
|
|
||||||
const result = await appendContractAttachments(
|
const result = await appendContractAttachments(
|
||||||
docId,
|
docId,
|
||||||
attachmentFiles,
|
attachmentFiles,
|
||||||
attachmentMergeMode,
|
attachmentMergeMode,
|
||||||
true,
|
true,
|
||||||
attachmentRemark || undefined,
|
attachmentRemark || undefined,
|
||||||
jwtToken
|
frontendJWT as string | undefined
|
||||||
);
|
);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw new Error(result.error);
|
throw new Error(result.error);
|
||||||
@@ -666,7 +665,7 @@ export default function RulesFiles() {
|
|||||||
>
|
>
|
||||||
查看
|
查看
|
||||||
</Button>
|
</Button>
|
||||||
{reviewType === 'contract' && file.status === 'Processed' && (
|
{file.fileTypeId === 1 && file.status === 'Processed' && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-xs px-2 py-1 h-7 mr-1 bg-primary text-white hover:bg-primary-dark rounded"
|
className="text-xs px-2 py-1 h-7 mr-1 bg-primary text-white hover:bg-primary-dark rounded"
|
||||||
|
|||||||
Reference in New Issue
Block a user