/** * AWS Configuration for Cognito-based S3 access * * This module handles AWS credentials through Cognito Identity Provider. * No AWS credentials are stored in code or exposed to the client. * * Environment Variables Required: * - VITE_AWS_REGION: AWS region (e.g., "us-east-2") * - VITE_AWS_COGNITO_IDENTITY_POOL_ID: Cognito Identity Pool ID (e.g., "us-east-2:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") * - VITE_S3_BUCKET: Default S3 bucket name (e.g., "ddy-first-bucket") */ import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity"; import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity"; export const AWS_REGION = import.meta.env.VITE_AWS_REGION || "us-east-2"; // Cognito pool ID is safe to embed in client code (not a secret) export const COGNITO_IDENTITY_POOL_ID = import.meta.env.VITE_AWS_COGNITO_IDENTITY_POOL_ID || "us-east-2:1387102d-5a87-4742-8a69-1baa70f37984"; export const DEFAULT_BUCKET = import.meta.env.VITE_S3_BUCKET || "ddy-first-bucket"; /** * Initialize S3 client with Cognito credentials * Uses unauthenticated Cognito Identity pool for public S3 access */ export function getS3Client() { const credentialsProvider = fromCognitoIdentityPool({ client: new CognitoIdentityClient({ region: AWS_REGION }), identityPoolId: COGNITO_IDENTITY_POOL_ID, }); return new S3Client({ region: AWS_REGION, credentials: credentialsProvider, }); } /** * Get S3 object as text (for JSON tilesets, metadata, etc.) */ export async function getS3ObjectAsText(bucket, key) { const s3Client = getS3Client(); if (!s3Client) { throw new Error("S3 client not initialized"); } try { const command = new GetObjectCommand({ Bucket: bucket, Key: key, }); const response = await s3Client.send(command); const text = await response.Body.transformToString(); return text; } catch (error) { console.error(`Failed to fetch S3 object s3://${bucket}/${key}:`, error); throw error; } } /** * Get S3 object as JSON */ export async function getS3ObjectAsJson(bucket, key) { const text = await getS3ObjectAsText(bucket, key); return JSON.parse(text); } /** * Build a direct S3 URL for tile assets * Note: This URL requires proper CORS configuration on S3 bucket * or the bucket must be public with proper authentication */ export function buildS3Url(bucket, key) { return `https://${bucket}.s3.${AWS_REGION}.amazonaws.com/${key}`; } /** * Rewrite URLs in a tileset JSON to use proper S3 paths * Handles both relative and absolute URLs */ export function rewriteTilesetUrls(tilesetJson, bucket, basePath) { const rewrite = (obj) => { if (!obj || typeof obj !== "object") return; if (Array.isArray(obj)) { obj.forEach(rewrite); return; } Object.keys(obj).forEach((key) => { const value = obj[key]; // Rewrite uri/url fields to point to S3 if ((key === "uri" || key === "url") && typeof value === "string") { // Skip external URLs if (value.startsWith("http://") || value.startsWith("https://")) { return; } // Skip data URLs if (value.startsWith("data:")) { return; } // Build S3 URL for relative paths const relativePath = value.startsWith("/") ? value.slice(1) : value; const s3Key = basePath ? `${basePath}/${relativePath}`.replace(/\/+/g, "/") : relativePath; obj[key] = buildS3Url(bucket, s3Key); } // Recursively rewrite nested objects if (typeof value === "object") { rewrite(value); } }); }; rewrite(tilesetJson); return tilesetJson; } /** * Load a tileset JSON file from S3 and rewrite its URLs */ export async function loadTilesetFromS3(bucket, tilesetPath, basePath) { try { const tilesetJson = await getS3ObjectAsJson(bucket, tilesetPath); return rewriteTilesetUrls(tilesetJson, bucket, basePath); } catch (error) { console.error(`Failed to load tileset from S3:`, error); throw error; } } /** * Get default S3 bucket name */ export function getDefaultBucket() { return DEFAULT_BUCKET; } /** * Get AWS region */ export function getAwsRegion() { return AWS_REGION; } /** * Validate AWS configuration */ export function validateAwsConfig() { return !!(AWS_REGION && COGNITO_IDENTITY_POOL_ID && DEFAULT_BUCKET); }