Skip to content

Commit 57f031e

Browse files
committed
Update CLI for static file uploads with Cloudflare cache purging support. Modified package.json to change the upload script and added a post-install message. Enhanced README with quick start instructions for LLM integration and detailed cache purging options, including new command-line flags and environment variable setups.
1 parent c4f3f7f commit 57f031e

5 files changed

Lines changed: 491 additions & 50 deletions

File tree

README.md

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ Install the component:
2121
npm install @get-convex/self-static-hosting
2222
```
2323

24+
### Quick Start with LLM
25+
26+
Get comprehensive integration instructions to paste into your AI assistant:
27+
28+
```bash
29+
npx @get-convex/self-static-hosting init
30+
```
31+
32+
This outputs all the code you need to integrate the component.
33+
34+
### Manual Setup
35+
2436
Add to your `convex/convex.config.ts`:
2537

2638
```ts
@@ -86,6 +98,7 @@ npx @get-convex/self-static-hosting upload [options]
8698
Options:
8799
-d, --dist <path> Path to dist directory (default: ./dist)
88100
-m, --module <name> Convex module name (default: staticHosting)
101+
--domain <name> Domain for Cloudflare cache purge (auto-detects zone ID)
89102
-h, --help Show help
90103
```
91104

@@ -190,29 +203,48 @@ Your app will be available at `https://yourdomain.com/app/`
190203
- `/app/assets/*` → Cache Level: Cache Everything, Edge TTL: 1 year
191204
- `/app/*` → Cache Level: Cache Everything, Edge TTL: 1 day
192205

193-
### Optional: Cache Purging
206+
### Cache Purging
194207

195-
To automatically purge Cloudflare cache on deploy:
208+
The CLI can automatically purge Cloudflare cache after deploying.
196209

197-
1. **Expose the cache purge action** in your `convex/staticHosting.ts`:
198-
```ts
199-
import { exposeCachePurgeAction } from "@get-convex/self-static-hosting";
200-
201-
export const { purgeCloudflareCache } = exposeCachePurgeAction();
202-
```
210+
**Option 1: Use `--domain` flag (easiest)**
203211

204-
2. **Get your Cloudflare credentials**:
205-
- Zone ID: Found on your domain's overview page
206-
- API Token: Create one at Account > API Tokens with "Cache Purge" permission
212+
```bash
213+
# First, login to Cloudflare via wrangler
214+
npx wrangler login
207215

208-
3. **Set environment variables** before deploying:
209-
```bash
210-
export CLOUDFLARE_ZONE_ID="your-zone-id"
211-
export CLOUDFLARE_API_TOKEN="your-api-token"
212-
npm run deploy:static
213-
```
216+
# Then deploy with your domain
217+
npx @get-convex/self-static-hosting upload --domain mysite.com
218+
```
214219

215-
The deploy script will automatically purge the cache after uploading new files.
220+
The CLI will auto-detect your zone ID and purge the cache.
221+
222+
**Option 2: Environment variables (for CI/CD)**
223+
224+
```bash
225+
export CLOUDFLARE_ZONE_ID="your-zone-id"
226+
export CLOUDFLARE_API_TOKEN="your-api-token"
227+
npx @get-convex/self-static-hosting upload
228+
```
229+
230+
To get these values:
231+
- Zone ID: Found on your domain's overview page in Cloudflare
232+
- API Token: Create at Account → API Tokens with "Cache Purge" permission
233+
234+
**Option 3: Via Convex function (for advanced CI/CD)**
235+
236+
Expose the cache purge action in your `convex/staticHosting.ts`:
237+
```ts
238+
import { exposeCachePurgeAction } from "@get-convex/self-static-hosting";
239+
240+
export const { purgeCloudflareCache } = exposeCachePurgeAction();
241+
```
242+
243+
Then call it from your CI/CD pipeline:
244+
```bash
245+
npx convex run staticHosting:purgeCloudflareCache \
246+
'{"zoneId": "...", "apiToken": "...", "purgeAll": true}'
247+
```
216248

217249
## Live Reload on Deploy
218250

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
],
1515
"type": "module",
1616
"bin": {
17-
"self-static-hosting": "./dist/cli/upload.js"
17+
"self-static-hosting": "./dist/cli/index.js"
1818
},
1919
"scripts": {
20+
"postinstall": "echo '\\n📦 @get-convex/self-static-hosting installed!\\n\\n Run this command to get LLM integration instructions:\\n npx @get-convex/self-static-hosting init\\n'",
2021
"dev": "run-p -r 'dev:*'",
2122
"dev:backend": "convex dev --typecheck-components",
2223
"dev:frontend": "cd example && vite --clearScreen false",
2324
"build:example": "cd example && vite build",
24-
"upload:static": "node dist/cli/upload.js --dist ./example/dist --module example",
25+
"upload:static": "node dist/cli/index.js upload --dist ./example/dist --module example",
2526
"deploy:static": "npm run build:example && npm run upload:static",
2627
"dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build:codegen' --initial",
2728
"predev": "path-exists .env.local dist || (npm run build && convex dev --once)",

src/cli/index.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env node
2+
/**
3+
* CLI entry point for @get-convex/self-static-hosting
4+
*
5+
* Commands:
6+
* init - Output integration instructions (LLM-friendly)
7+
* upload - Upload static files to Convex
8+
*/
9+
10+
import { spawn } from "child_process";
11+
import { fileURLToPath } from "url";
12+
import { dirname, join } from "path";
13+
14+
const __dirname = dirname(fileURLToPath(import.meta.url));
15+
16+
const command = process.argv[2];
17+
const args = process.argv.slice(3);
18+
19+
if (command === "init") {
20+
// Run init command
21+
const initPath = join(__dirname, "init.js");
22+
await import(initPath);
23+
} else if (command === "upload" || !command || command === "--help" || command === "-h") {
24+
// Run upload command (default)
25+
const uploadPath = join(__dirname, "upload.js");
26+
const uploadArgs = command === "upload" ? args : process.argv.slice(2);
27+
28+
// Re-execute with upload script
29+
const child = spawn(process.execPath, [uploadPath, ...uploadArgs], {
30+
stdio: "inherit",
31+
});
32+
33+
child.on("exit", (code) => {
34+
process.exit(code ?? 0);
35+
});
36+
} else {
37+
console.error(`Unknown command: ${command}`);
38+
console.error("");
39+
console.error("Available commands:");
40+
console.error(" init Output integration instructions for your LLM");
41+
console.error(" upload Upload static files to Convex storage");
42+
console.error("");
43+
console.error("Examples:");
44+
console.error(" npx @get-convex/self-static-hosting init");
45+
console.error(" npx @get-convex/self-static-hosting upload --dist ./dist");
46+
process.exit(1);
47+
}

src/cli/init.ts

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Output integration instructions for LLMs.
4+
*
5+
* Usage:
6+
* npx @get-convex/self-static-hosting init
7+
*/
8+
9+
const instructions = `
10+
# Convex Self Static Hosting - Integration Instructions
11+
12+
You are integrating the @get-convex/self-static-hosting component into a Convex app.
13+
This component enables hosting static files (React/Vite apps) directly on Convex.
14+
15+
## What This Component Does
16+
17+
- Stores static files in Convex storage
18+
- Serves files via HTTP actions with proper MIME types
19+
- Supports SPA routing (fallback to index.html)
20+
- Smart caching: hashed assets cached forever, HTML revalidates
21+
- ETag support for efficient cache revalidation
22+
- Live reload notifications when new deployments happen
23+
- Optional Cloudflare CDN integration
24+
25+
## Files to Create/Modify
26+
27+
### 1. convex/convex.config.ts (create or modify)
28+
29+
\`\`\`typescript
30+
import { defineApp } from "convex/server";
31+
import selfStaticHosting from "@get-convex/self-static-hosting/convex.config";
32+
33+
const app = defineApp();
34+
app.use(selfStaticHosting);
35+
36+
export default app;
37+
\`\`\`
38+
39+
### 2. convex/staticHosting.ts (create)
40+
41+
\`\`\`typescript
42+
import { components } from "./_generated/api";
43+
import {
44+
exposeUploadApi,
45+
exposeDeploymentQuery,
46+
exposeCachePurgeAction,
47+
} from "@get-convex/self-static-hosting";
48+
49+
// Internal functions for secure uploads (only callable via CLI)
50+
export const { generateUploadUrl, recordAsset, gcOldAssets, listAssets } =
51+
exposeUploadApi(components.selfStaticHosting);
52+
53+
// Public query for live reload notifications
54+
export const { getCurrentDeployment } =
55+
exposeDeploymentQuery(components.selfStaticHosting);
56+
57+
// Optional: Cloudflare cache purge (for CI/CD)
58+
export const { purgeCloudflareCache } = exposeCachePurgeAction();
59+
\`\`\`
60+
61+
### 3. convex/http.ts (create or modify)
62+
63+
\`\`\`typescript
64+
import { httpRouter } from "convex/server";
65+
import { registerStaticRoutes } from "@get-convex/self-static-hosting";
66+
import { components } from "./_generated/api";
67+
68+
const http = httpRouter();
69+
70+
// Option A: Serve at root (if no other HTTP routes)
71+
registerStaticRoutes(http, components.selfStaticHosting);
72+
73+
// Option B: Serve at /app/ prefix (recommended if you have API routes)
74+
// registerStaticRoutes(http, components.selfStaticHosting, {
75+
// pathPrefix: "/app",
76+
// });
77+
78+
// Add other HTTP routes here if needed
79+
// http.route({ path: "/api/webhook", method: "POST", handler: ... });
80+
81+
export default http;
82+
\`\`\`
83+
84+
### 4. package.json scripts (add)
85+
86+
\`\`\`json
87+
{
88+
"scripts": {
89+
"build": "vite build",
90+
"deploy:static": "npm run build && npx @get-convex/self-static-hosting upload"
91+
}
92+
}
93+
\`\`\`
94+
95+
If using a path prefix, specify the module:
96+
\`\`\`json
97+
{
98+
"scripts": {
99+
"deploy:static": "npm run build && npx @get-convex/self-static-hosting upload --module staticHosting"
100+
}
101+
}
102+
\`\`\`
103+
104+
### 5. src/main.tsx (modify entry point)
105+
106+
\`\`\`typescript
107+
import React from "react";
108+
import ReactDOM from "react-dom/client";
109+
import { ConvexProvider, ConvexReactClient } from "convex/react";
110+
import { getConvexUrlWithFallback } from "@get-convex/self-static-hosting";
111+
import App from "./App";
112+
113+
// Auto-detects Convex URL when deployed to *.convex.site
114+
const convexUrl = getConvexUrlWithFallback(import.meta.env.VITE_CONVEX_URL);
115+
const convex = new ConvexReactClient(convexUrl);
116+
117+
ReactDOM.createRoot(document.getElementById("root")!).render(
118+
<React.StrictMode>
119+
<ConvexProvider client={convex}>
120+
<App />
121+
</ConvexProvider>
122+
</React.StrictMode>
123+
);
124+
\`\`\`
125+
126+
### 6. src/App.tsx (optional: add live reload banner)
127+
128+
\`\`\`typescript
129+
import { UpdateBanner } from "@get-convex/self-static-hosting/react";
130+
import { api } from "../convex/_generated/api";
131+
132+
function App() {
133+
return (
134+
<div>
135+
{/* Shows banner when new deployment is available */}
136+
<UpdateBanner
137+
getCurrentDeployment={api.staticHosting.getCurrentDeployment}
138+
message="New version available!"
139+
buttonText="Refresh"
140+
/>
141+
142+
{/* Rest of your app */}
143+
</div>
144+
);
145+
}
146+
\`\`\`
147+
148+
Or use the hook for custom UI:
149+
\`\`\`typescript
150+
import { useDeploymentUpdates } from "@get-convex/self-static-hosting/react";
151+
import { api } from "../convex/_generated/api";
152+
153+
function App() {
154+
const { updateAvailable, reload, dismiss } = useDeploymentUpdates(
155+
api.staticHosting.getCurrentDeployment
156+
);
157+
158+
// Custom update notification UI
159+
}
160+
\`\`\`
161+
162+
## Deployment
163+
164+
\`\`\`bash
165+
# Login to Convex (first time)
166+
npx convex login
167+
168+
# Push Convex functions
169+
npx convex dev --once
170+
171+
# Build and deploy static files
172+
npm run deploy:static
173+
174+
# Your app is now live at:
175+
# https://your-deployment.convex.site
176+
# (or https://your-deployment.convex.site/app/ if using path prefix)
177+
\`\`\`
178+
179+
## Optional: Cloudflare CDN
180+
181+
For production with custom domain and edge caching:
182+
183+
\`\`\`bash
184+
# Login to Cloudflare
185+
npx wrangler login
186+
187+
# Deploy with automatic cache purge
188+
npx @get-convex/self-static-hosting upload --domain yourdomain.com
189+
\`\`\`
190+
191+
Or for CI/CD, set environment variables:
192+
\`\`\`bash
193+
export CLOUDFLARE_ZONE_ID="your-zone-id"
194+
export CLOUDFLARE_API_TOKEN="your-api-token"
195+
npm run deploy:static
196+
\`\`\`
197+
198+
## Important Notes
199+
200+
1. The upload functions are INTERNAL - they can only be called via \`npx convex run\`, not from the public internet
201+
2. Static files are stored in the app's storage (not the component's) for proper isolation
202+
3. Hashed assets (e.g., main-abc123.js) get immutable caching; HTML files always revalidate
203+
4. The component supports SPA routing - routes without file extensions serve index.html
204+
`;
205+
206+
console.log(instructions);

0 commit comments

Comments
 (0)