File size: 4,493 Bytes
be9292f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa00da8
be9292f
aa00da8
 
be9292f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aa00da8
be9292f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
 * 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);
}