feature: update to deno 2 and initializeClient requirement

This commit is contained in:
Alexander Nicholson 4584443+DragonStuff@users.noreply.github.com
2024-11-20 16:54:38 +09:00
parent e99a065f77
commit aff7414f57
2 changed files with 33 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
# Use the official Deno image # Use the official Deno image
FROM denoland/deno:1.46.3 FROM denoland/deno:2.0.6
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app

View File

@@ -2,6 +2,7 @@ import { ListObjectsOptions, ListObjectsResult, StorageObject } from "../types/m
import { S3, type ListObjectsV2Request } from "https://deno.land/x/aws_api@v0.8.1/services/s3/mod.ts"; import { S3, type ListObjectsV2Request } from "https://deno.land/x/aws_api@v0.8.1/services/s3/mod.ts";
import { ApiFactory } from "https://deno.land/x/aws_api@v0.8.1/client/mod.ts"; import { ApiFactory } from "https://deno.land/x/aws_api@v0.8.1/client/mod.ts";
import { getSignedUrl } from "https://deno.land/x/aws_s3_presign@2.2.1/mod.ts"; import { getSignedUrl } from "https://deno.land/x/aws_s3_presign@2.2.1/mod.ts";
import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.48/deno-dom-wasm.ts";
interface Credentials { interface Credentials {
awsAccessKeyId: string; awsAccessKeyId: string;
@@ -11,10 +12,14 @@ interface Credentials {
export class StorageService { export class StorageService {
private client: S3; private client: S3;
private credentials: Credentials; private credentials!: Credentials;
constructor(private bucket: string, private region: string) { constructor(private bucket: string, private region: string) {
this.credentials = this.resolveCredentials(); this.initializeClient();
}
private async initializeClient() {
this.credentials = await this.resolveCredentials();
const factory = new ApiFactory({ const factory = new ApiFactory({
region: this.region, region: this.region,
credentials: this.credentials, credentials: this.credentials,
@@ -23,6 +28,13 @@ export class StorageService {
this.client = new S3(factory); this.client = new S3(factory);
} }
// Make sure any method that uses this.client waits for initialization
private async ensureInitialized() {
if (!this.client) {
await this.initializeClient();
}
}
private async fetchInstanceProfileCredentials(): Promise<[Credentials | null, string | null]> { private async fetchInstanceProfileCredentials(): Promise<[Credentials | null, string | null]> {
try { try {
const metadataUrl = "http://169.254.169.254/latest/meta-data/iam/security-credentials/"; const metadataUrl = "http://169.254.169.254/latest/meta-data/iam/security-credentials/";
@@ -98,6 +110,7 @@ export class StorageService {
} }
async getSignedUrl(key: string, expiresIn = 3600): Promise<string> { async getSignedUrl(key: string, expiresIn = 3600): Promise<string> {
await this.ensureInitialized();
return await getSignedUrl({ return await getSignedUrl({
accessKeyId: this.credentials.awsAccessKeyId, accessKeyId: this.credentials.awsAccessKeyId,
secretAccessKey: this.credentials.awsSecretKey, secretAccessKey: this.credentials.awsSecretKey,
@@ -109,6 +122,7 @@ export class StorageService {
} }
async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> { async listObjects(options: ListObjectsOptions): Promise<ListObjectsResult> {
await this.ensureInitialized();
try { try {
const params: ListObjectsV2Request = { const params: ListObjectsV2Request = {
Bucket: this.bucket, Bucket: this.bucket,
@@ -160,10 +174,14 @@ export class StorageService {
} }
class WebIdentityCredentials { class WebIdentityCredentials {
private parser: DOMParser;
constructor( constructor(
private roleArn: string, private roleArn: string,
private token: string, private token: string,
) {} ) {
this.parser = new DOMParser();
}
async getCredentials(): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> { async getCredentials(): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> {
const params = new URLSearchParams({ const params = new URLSearchParams({
@@ -183,15 +201,20 @@ class WebIdentityCredentials {
} }
const xml = await response.text(); const xml = await response.text();
const result = new DOMParser().parseFromString(xml, "text/xml"); const result = this.parser.parseFromString(xml, "text/xml");
if (!result) throw new Error("Failed to parse XML response");
const credentials = result.querySelector("Credentials"); const credentials = result.querySelector("Credentials");
if (!credentials) throw new Error("No credentials in response"); if (!credentials) throw new Error("No credentials in response");
return { const accessKeyId = credentials.querySelector("AccessKeyId")?.textContent;
accessKeyId: credentials.querySelector("AccessKeyId")?.textContent ?? "", const secretAccessKey = credentials.querySelector("SecretAccessKey")?.textContent;
secretAccessKey: credentials.querySelector("SecretAccessKey")?.textContent ?? "", const sessionToken = credentials.querySelector("SessionToken")?.textContent;
sessionToken: credentials.querySelector("SessionToken")?.textContent ?? "",
}; if (!accessKeyId || !secretAccessKey || !sessionToken) {
throw new Error("Missing required credential fields in response");
}
return { accessKeyId, secretAccessKey, sessionToken };
} }
} }