diff --git a/lib/sts.ts b/lib/sts.ts index 661bcfe..805f1ee 100644 --- a/lib/sts.ts +++ b/lib/sts.ts @@ -1,6 +1,9 @@ -import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; -import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@aws-sdk/types"; -import type { SiteConfig } from "~/types/config"; +import { AssumeRoleCommand, STSClient } from '@aws-sdk/client-sts' +import type { + AwsCredentialIdentity, + AwsCredentialIdentityProvider +} from '@aws-sdk/types' +import type { SiteConfig } from '~/types/config' /** * 获取 STS 临时凭证并返回 @@ -15,30 +18,30 @@ export async function getStsToken( roleArn: string, customConfig?: SiteConfig ) { - const siteConfig = customConfig || (useNuxtApp().$siteConfig as SiteConfig); + const siteConfig = customConfig || (useNuxtApp().$siteConfig as SiteConfig) - console.log('S3 siteConfig', siteConfig); + console.log('S3 siteConfig', siteConfig) // 1. 创建 STS 客户端 const stsClient = new STSClient({ - endpoint: siteConfig.s3.endpoint + (process.env.BASE_URL || "/rustfs/console/") , + endpoint: siteConfig.s3.endpoint, region: siteConfig.s3.region || 'us-east-1', credentials: credentials - }); + }) // 2. 构建 AssumeRole 请求 // 你可以根据需要额外添加 DurationSeconds、Policy 等参数 const command = new AssumeRoleCommand({ RoleArn: roleArn, - RoleSessionName: "console", // 自定义角色会话名称 - DurationSeconds: siteConfig.session?.durationSeconds || 3600 * 12, // 临时凭证有效期 - }); + RoleSessionName: 'console', // 自定义角色会话名称 + DurationSeconds: siteConfig.session?.durationSeconds || 3600 * 12 // 临时凭证有效期 + }) - const response = await stsClient.send(command); + const response = await stsClient.send(command) if (!response.Credentials) { - throw new Error("Failed to retrieve credentials"); + throw new Error('Failed to retrieve credentials') } - return response.Credentials; + return response.Credentials } diff --git a/plugins/03.s3.ts b/plugins/03.s3.ts index 67963b6..2c4d332 100644 --- a/plugins/03.s3.ts +++ b/plugins/03.s3.ts @@ -1,87 +1,99 @@ -import { S3Client } from "@aws-sdk/client-s3"; -import type { SiteConfig } from "~/types/config"; -import type { DeserializeHandler, DeserializeHandlerArguments, DeserializeHandlerOutput } from "@aws-sdk/types"; +import { S3Client } from '@aws-sdk/client-s3' +import type { SiteConfig } from '~/types/config' +import type { + DeserializeHandler, + DeserializeHandlerArguments, + DeserializeHandlerOutput +} from '@aws-sdk/types' interface S3Response { response?: { - body?: string; - }; - [key: string]: any; + body?: string + } + [key: string]: any } export default defineNuxtPlugin({ - name: "s3-client", + name: 's3-client', setup(nuxtApp) { - const { credentials, isAuthenticated } = useAuth(); - const siteConfig = nuxtApp.$siteConfig as SiteConfig; + const { credentials, isAuthenticated } = useAuth() + const siteConfig = nuxtApp.$siteConfig as SiteConfig if (!isAuthenticated || !credentials.value) { - return; + return } const client = new S3Client({ - endpoint: siteConfig.s3.endpoint + (process.env.BASE_URL || "/rustfs/console/"), - region: siteConfig.s3.region || "us-east-1", + endpoint: siteConfig.s3.endpoint, + region: siteConfig.s3.region || 'us-east-1', forcePathStyle: true, // https://github.com/aws/aws-sdk-js-v3/issues/6834#issuecomment-2611346849 - requestChecksumCalculation: "WHEN_REQUIRED", + requestChecksumCalculation: 'WHEN_REQUIRED', credentials: { - accessKeyId: credentials.value?.AccessKeyId || "", - secretAccessKey: credentials.value?.SecretAccessKey || "", - sessionToken: credentials.value?.SessionToken || "", - }, - }); + accessKeyId: credentials.value?.AccessKeyId || '', + secretAccessKey: credentials.value?.SecretAccessKey || '', + sessionToken: credentials.value?.SessionToken || '' + } + }) // 添加中间件处理XML响应和401错误 client.middlewareStack.add( (next: DeserializeHandler) => - async (args: DeserializeHandlerArguments): Promise> => { + async ( + args: DeserializeHandlerArguments + ): Promise> => { try { - const response = (await next(args)) as S3Response; + const response = (await next(args)) as S3Response // 检查响应是否为XML格式 - if (response.response?.body && typeof response.response.body === "string") { - const body = response.response.body.trim(); + if ( + response.response?.body && + typeof response.response.body === 'string' + ) { + const body = response.response.body.trim() // 检查是否是空的XML响应 if (body.match(/^<\?xml[^>]*\?><[^>]*><\/[^>]*>$/)) { // 从XML标签名提取属性名 - const tagName = body.match(/<([^>]*)><\/\1>/)?.[1]; + const tagName = body.match(/<([^>]*)><\/\1>/)?.[1] if (tagName) { // 将XML标签名转换为驼峰命名 - const propertyName = tagName.replace(/(?:^|_)([a-z])/g, (_, letter) => letter.toUpperCase()); + const propertyName = tagName.replace( + /(?:^|_)([a-z])/g, + (_, letter) => letter.toUpperCase() + ) // 返回null,保持response结构 return { response: response.response, - [propertyName]: null, - } as unknown as DeserializeHandlerOutput; + [propertyName]: null + } as unknown as DeserializeHandlerOutput } } } - return response as DeserializeHandlerOutput; + return response as DeserializeHandlerOutput } catch (error: any) { if (error?.$metadata?.httpStatusCode === 401) { - await useAuth().logoutAndRedirect(); + await useAuth().logoutAndRedirect() } // 处理 S3 客户端错误,优先抛出 error.message 作为新的 Error if (error?.Code) { - throw new Error(error.Code); + throw new Error(error.Code) } - throw error; + throw error } }, { - step: "deserialize", - name: "handleXmlResponse", + step: 'deserialize', + name: 'handleXmlResponse' } - ); + ) return { provide: { - s3Client: client, - }, - }; - }, -}); + s3Client: client + } + } + } +})