This updates the example to compute SHA-256 from a selected GLB/GLTF file, create/link asset metadata, upload with signed URL, and use share links plus visibility toggling. Made-with: Cursor
This commit is contained in:
@@ -114,7 +114,7 @@ When `SERVICE_PUBLIC_KEY` (or `ADMIN_PUBLIC_KEY`) is set, users can register wit
|
||||
|
||||
Server keys are generated with `./bin/gen-server-keys.sh` and stored in `etc/`.
|
||||
|
||||
## Example (TypeScript app)
|
||||
## Example: place and upload a 3D object (`.glb`)
|
||||
|
||||
```ts
|
||||
import { GeoApiClient } from "../libs/geo-api-client/dist/index.js";
|
||||
@@ -140,28 +140,53 @@ await client.loginWithSignature(keys.publicKey, keys.privateKey);
|
||||
const created = await client.createCollection("My Places");
|
||||
const feature = await client.createPointFeature(created.id, -16.6291, 28.4636, { name: "Santa Cruz" });
|
||||
|
||||
// 3D/image asset flow
|
||||
// Assume this comes from: <input id="modelFile" type="file" accept=".glb,.gltf" />
|
||||
const fileInput = document.getElementById("modelFile") as HTMLInputElement;
|
||||
const file = fileInput.files?.[0];
|
||||
if (!file) throw new Error("Select a .glb/.gltf file first");
|
||||
|
||||
const toSha256Hex = async (f: File): Promise<string> => {
|
||||
const buffer = await f.arrayBuffer();
|
||||
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
||||
return Array.from(new Uint8Array(digest))
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
};
|
||||
|
||||
const checksum = await toSha256Hex(file);
|
||||
const ext = file.name.toLowerCase().endsWith(".gltf") ? "gltf" : "glb";
|
||||
|
||||
// Create metadata (or reuse existing by checksum+ext) and link to feature
|
||||
const asset = await client.createOrLinkAsset({
|
||||
featureId: feature.id,
|
||||
checksum: "sha256hex...",
|
||||
ext: "glb",
|
||||
checksum,
|
||||
ext,
|
||||
kind: "3d",
|
||||
mimeType: "model/gltf-binary",
|
||||
sizeBytes: 1024,
|
||||
name: "Palm Tree",
|
||||
description: "Low-poly model",
|
||||
mimeType: file.type || (ext === "gltf" ? "model/gltf+json" : "model/gltf-binary"),
|
||||
sizeBytes: file.size,
|
||||
name: "Palm Tree Model",
|
||||
description: "3D object placed on map",
|
||||
isPublic: true,
|
||||
});
|
||||
const signed = await client.getAssetSignedUploadUrl(asset.asset.id, "model/gltf-binary");
|
||||
// fetch(signed.url, { method: signed.method, body: file })
|
||||
|
||||
// Upload binary to object storage through signed URL
|
||||
const signed = await client.getAssetSignedUploadUrl(asset.asset.id, asset.asset.mimeType);
|
||||
await fetch(signed.url, {
|
||||
method: signed.method,
|
||||
headers: asset.asset.mimeType ? { "Content-Type": asset.asset.mimeType } : undefined,
|
||||
body: file,
|
||||
});
|
||||
|
||||
// Read shareable relative link from feature payload
|
||||
const features = await client.listFeatures(created.id);
|
||||
const firstAsset = features.features[0]?.properties?.assets?.[0];
|
||||
if (firstAsset) {
|
||||
const shareUrl = client.resolveRelativeLink(firstAsset.link);
|
||||
console.log("Share URL:", shareUrl);
|
||||
}
|
||||
console.log(features);
|
||||
|
||||
// Optional: owner can disable public access later
|
||||
await client.setAssetVisibility(asset.asset.id, false);
|
||||
```
|
||||
|
||||
## Security notes
|
||||
|
||||
Reference in New Issue
Block a user