Skip to content

Commit 5734c97

Browse files
marc0oloclaude
andauthored
fix(hosting/photo-storage): restore asset-canister recipe and fix canister-ID discovery (#1464)
* fix(hosting/photo-storage): restore asset-canister recipe and fix canister-ID discovery The example was broken twice over: 1. The switch to @dfinity/static-site (#1449) removed the legacy asset canister API (list/store/create_batch/commit_batch) that AssetManager from @icp-sdk/canisters/assets depends on — the app's entire purpose. Revert to @dfinity/asset-canister@v2.2.1, the only canister supporting programmatic uploads, with a comment explaining the exception (#1459). 2. The dfx-era canister-ID parsing broke on icp-cli's name-based frontend URLs (frontend.local.localhost) — Principal.fromText received "frontend.local" and threw at module load (white screen). Replace the hostname regex, ic0.app heuristic, and fetchRootKey() with the ic_env cookie pattern used by hello_world (safeGetCanisterEnv), which works on both URL forms and on mainnet. Also fix the README authorize command (canister is named frontend, not photo-storage) and point deploy instructions at the URL icp deploy prints. Verified end-to-end locally: gallery renders, uploads work after authorize, photos persist across reloads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hosting/photo-storage): address review — drop cookie-less fallback, fix README filename The ?canisterId= query-param fallback ran without the ic_env cookie and therefore without the local root key — a half-broken path nothing uses (the app is only ever served from the canister, which always sets the cookie; there is no dev-server flow). Require the cookie and fail with a clear error instead. Deliberately no fetchRootKey(): that is legacy behavior we no longer recommend — the root key comes from the certified ic_env cookie. Also fix the README warning to reference src/App.jsx (renamed from App.js in the Vite migration). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 3113452 commit 5734c97

3 files changed

Lines changed: 25 additions & 13 deletions

File tree

hosting/photo-storage/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ Deploy the canisters:
3838
icp deploy
3939
```
4040

41-
The URL for the frontend depends on the canister ID. When deployed, the URL will look like this:
41+
Open the frontend URL printed by `icp deploy`, e.g.:
4242

4343
```
44-
http://{canister_id}.localhost:8000
44+
http://frontend.local.localhost:8000
4545
```
4646

4747
To authorize an identity to upload files, it must be authorized first:
4848

4949
```bash
50-
icp canister call photo-storage authorize '(principal "535yc-uxytb-gfk7h-tny7p-vjkoe-i4krp-3qmcl-uqfgr-cpgej-yqtjq-rqe")'
50+
icp canister call frontend authorize '(principal "535yc-uxytb-gfk7h-tny7p-vjkoe-i4krp-3qmcl-uqfgr-cpgej-yqtjq-rqe")'
5151
```
5252

53-
> **Warning:** This example uses a hardcoded identity (defined in `src/App.js`). Before deploying to the IC mainnet, replace it with a proper authentication method such as [Internet Identity](https://docs.internetcomputer.org/building-apps/authentication/integrate-internet-identity).
53+
> **Warning:** This example uses a hardcoded identity (defined in `src/App.jsx`). Before deploying to the IC mainnet, replace it with a proper authentication method such as [Internet Identity](https://docs.internetcomputer.org/guides/authentication/internet-identity).
5454
5555
Stop the local network when done:
5656

hosting/photo-storage/icp.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
canisters:
22
- name: frontend
33
recipe:
4-
type: "@dfinity/static-site@v0.3.1"
4+
# Deliberately stays on the legacy asset-canister recipe: this example
5+
# demonstrates programmatic uploads via AssetManager, which the
6+
# static-site (certified-assets) canister does not support.
7+
# See https://github.com/dfinity/examples/issues/1459
8+
type: "@dfinity/asset-canister@v2.2.1"
59
configuration:
610
dir: dist
711
build:

hosting/photo-storage/src/App.jsx

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {Ed25519KeyIdentity} from '@icp-sdk/core/identity';
22
import {HttpAgent} from '@icp-sdk/core/agent';
3+
import {safeGetCanisterEnv} from '@icp-sdk/core/agent/canister-env';
34
import {AssetManager} from '@icp-sdk/canisters/assets';
45
import {useEffect, useState} from "react";
56
import Masonry from "react-masonry-css";
@@ -8,16 +9,23 @@ import './App.css';
89
// Hardcoded principal: 535yc-uxytb-gfk7h-tny7p-vjkoe-i4krp-3qmcl-uqfgr-cpgej-yqtjq-rqe
910
// Should be replaced with authentication method e.g. Internet Identity when deployed on IC
1011
const identity = Ed25519KeyIdentity.generate(new Uint8Array(Array.from({length: 32}).fill(0)));
11-
const isLocal = !window.location.host.endsWith('ic0.app');
12-
const agent = HttpAgent.createSync({
13-
host: isLocal ? `http://127.0.0.1:${window.location.port}` : 'https://ic0.app', identity,
14-
});
15-
if (isLocal) {
16-
await agent.fetchRootKey();
12+
13+
// The ic_env cookie is set by the asset canister on all HTML responses. It
14+
// contains the replica root key and the PUBLIC_* canister environment
15+
// variables, so the app finds its own canister ID regardless of which URL the
16+
// gateway serves it under (canister-id-based or name-based).
17+
const canisterEnv = safeGetCanisterEnv();
18+
const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:frontend"];
19+
20+
if (!canisterId) {
21+
throw new Error("Canister ID for 'frontend' not found. Run 'icp deploy' and open the URL it prints.");
1722
}
1823

19-
// Canister id can be fetched from URL since frontend in this example is hosted in the same canister as file upload
20-
const canisterId = new URLSearchParams(window.location.search).get('canisterId') ?? /(.*?)(?:\.raw)?\.ic0.app/.exec(window.location.host)?.[1] ?? /(.*)\.localhost/.exec(window.location.host)?.[1];
24+
const agent = HttpAgent.createSync({
25+
host: window.location.origin,
26+
rootKey: canisterEnv?.IC_ROOT_KEY,
27+
identity,
28+
});
2129

2230
// Create asset manager instance for above asset canister
2331
const assetManager = new AssetManager({canisterId, agent});

0 commit comments

Comments
 (0)