miris

miris.astralcity.studio

GitHub Pages deployment for Miris demos.

Live at: https://miris.astralcity.studio

How to deploy

  1. Build your project:
    npm run build
    
  2. Copy the build output into a folder in this repo:
    cp -r deploy/* /Users/roberto/io/git/astralcity/miris/stress-test/
    
  3. Commit and push:
    cd /Users/roberto/io/git/astralcity/miris
    git add -A && git commit -m "update" && git push
    

GitHub Pages deploys automatically on push (~30 seconds).

Adding a new demo

Create a new folder at the root (e.g. my-demo/) and copy your built files into it. It will be available at https://miris.astralcity.studio/my-demo/.

Large assets (USDZ)

GitHub Pages has a 100MB repo size limit, so large files (USDZ models) cannot be committed here.

Where they are hosted

Large assets are uploaded as GitHub Release assets in a separate public repo:

Why a CORS proxy is needed

GitHub Release download URLs don’t include CORS headers, so browsers block fetch() requests to them. A Cloudflare Worker acts as a CORS proxy:

How it works in code (src/assets.js)

const ASSET_MODE = "github"; // "local" for quick testing with files in public/usdz/

function assetUrl(filename) {
  if (ASSET_MODE === "local") return `./usdz/${filename}`;
  if (import.meta.env.DEV) return `/gh-assets/${filename}`;  // Vite proxy (local dev)
  return `https://gh-cors-proxy.astral-city-assets.workers.dev/${filename}`; // Production
}

Adding new assets

  1. Download or prepare your .usdz file
  2. Upload it to the existing release:
    gh release upload v1.0.0-assets myfile.usdz --repo Astral-City/demo-assets
    
  3. Reference it in assets.js:
    usdz: assetUrl("myfile.usdz"),
    

Cloudflare Worker source

Deployed at gh-cors-proxy.astral-city-assets.workers.dev. To redeploy or modify:

cd /tmp/gh-cors-proxy
npx wrangler deploy

Worker code:

const GITHUB_RELEASE =
  "https://github.com/Astral-City/demo-assets/releases/download/v1.0.0-assets";

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const filename = url.pathname.slice(1);
    if (!filename) return new Response("Usage: /<filename>", { status: 400 });
    if (request.method === "OPTIONS") return new Response(null, { headers: corsHeaders() });

    const resp = await fetch(`${GITHUB_RELEASE}/${filename}`, { redirect: "follow" });
    if (!resp.ok) return new Response("Not found", { status: resp.status, headers: corsHeaders() });

    return new Response(resp.body, {
      status: 200,
      headers: {
        ...corsHeaders(),
        "content-type": resp.headers.get("content-type") || "application/octet-stream",
        "content-length": resp.headers.get("content-length"),
        "cache-control": "public, max-age=86400",
      },
    });
  },
};

function corsHeaders() {
  return {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, HEAD, OPTIONS",
    "Access-Control-Allow-Headers": "*",
  };
}

Notes