Skip to content

Commit 5fed2b8

Browse files
committed
Add public file library feature
- Add library command to deploy a public file browser - Add worker command to list R2 files via API - Create library HTML with file grid display - Auto-configures PUBLIC_URL from your R2 settings
1 parent 797045d commit 5fed2b8

2 files changed

Lines changed: 179 additions & 0 deletions

File tree

bin/quick-share.js

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,184 @@ program
107107
);
108108
});
109109

110+
program
111+
.command("library")
112+
.alias("lib")
113+
.description("Deploy a public file library to Cloudflare Pages")
114+
.option(
115+
"-n, --name <name>",
116+
"Project name for Cloudflare Pages",
117+
"quick-share-library",
118+
)
119+
.option("-d, --domain <domain>", "Custom domain (optional)")
120+
.action(async (options) => {
121+
console.log(chalk.blue("⚡ Deploying File Library...\n"));
122+
123+
// Check for wrangler
124+
try {
125+
execSync("which wrangler", { stdio: "ignore" });
126+
} catch (e) {
127+
console.log(chalk.yellow("Installing wrangler..."));
128+
execSync("npm install -g wrangler", { stdio: "inherit" });
129+
}
130+
131+
// Check if logged in
132+
try {
133+
execSync("wrangler whoami", { stdio: "ignore" });
134+
} catch (e) {
135+
console.log(chalk.red("Please run: wrangler login"));
136+
process.exit(1);
137+
}
138+
139+
// Create temp directory
140+
const tempDir = require("os").tmpdir();
141+
const libDir = path.join(__dirname, "..", "assets", "library");
142+
const deployDir = path.join(tempDir, "quick-share-lib-deploy");
143+
144+
// Copy library files
145+
execSync(`rm -rf "${deployDir}" && cp -r "${libDir}" "${deployDir}"`);
146+
147+
// Update config in index.html
148+
const config = validateConfig();
149+
const indexPath = path.join(deployDir, "index.html");
150+
let indexHtml = fs.readFileSync(indexPath, "utf8");
151+
indexHtml = indexHtml.replace(
152+
/const PUBLIC_URL = '.*';/,
153+
`const PUBLIC_URL = '${config.publicUrl}';`,
154+
);
155+
fs.writeFileSync(indexPath, indexHtml);
156+
157+
console.log(chalk.blue("Deploying to Cloudflare Pages..."));
158+
159+
try {
160+
execSync(
161+
`cd "${deployDir}" && wrangler pages project list 2>/dev/null | grep -q "${options.name}" || wrangler pages project create "${options.name}"`,
162+
{ stdio: "pipe" },
163+
);
164+
} catch (e) {
165+
// Project might already exist
166+
}
167+
168+
execSync(
169+
`cd "${deployDir}" && wrangler pages deploy . --project-name="${options.name}"`,
170+
{ stdio: "inherit" },
171+
);
172+
173+
console.log(chalk.green("\n✓ Library deployed!"));
174+
console.log(chalk.blue("\nYour library is live at:"));
175+
console.log(chalk.white(` https://${options.name}.pages.dev`));
176+
177+
if (options.domain) {
178+
console.log(chalk.blue("\nAdding custom domain..."));
179+
execSync(
180+
`wrangler pages domain add "${options.name}" "${options.domain}"`,
181+
{ stdio: "inherit" },
182+
);
183+
console.log(chalk.green(`✓ Domain ${options.domain} connected!`));
184+
}
185+
186+
// Cleanup
187+
execSync(`rm -rf "${deployDir}"`);
188+
189+
console.log(chalk.blue("\nNext steps:"));
190+
console.log(
191+
" 1. Deploy a Worker to list R2 files (see: quick-share library --worker)",
192+
);
193+
console.log(" 2. Update WORKER_URL in your library to use the worker");
194+
});
195+
196+
program
197+
.command("worker")
198+
.description("Deploy a Cloudflare Worker that lists R2 files")
199+
.option("-n, --name <name>", "Worker name", "quick-share-api")
200+
.action(async (options) => {
201+
console.log(chalk.blue("⚡ Deploying API Worker...\n"));
202+
203+
// Check for wrangler
204+
try {
205+
execSync("which wrangler", { stdio: "ignore" });
206+
} catch (e) {
207+
console.log(chalk.yellow("Installing wrangler..."));
208+
execSync("npm install -g wrangler", { stdio: "inherit" });
209+
}
210+
211+
const config = validateConfig();
212+
const workerContent = `addEventListener("fetch", (event) => {
213+
event.respondWith(handleRequest(event.request));
214+
});
215+
216+
async function handleRequest(request) {
217+
const url = new URL(request.url);
218+
219+
const corsHeaders = {
220+
"Access-Control-Allow-Origin": "*",
221+
"Access-Control-Allow-Methods": "GET, OPTIONS",
222+
};
223+
224+
if (request.method === "OPTIONS") {
225+
return new Response(null, { headers: corsHeaders });
226+
}
227+
228+
if (url.pathname !== "/files" && url.pathname !== "/files.json") {
229+
return new Response("Not Found", { status: 404 });
230+
}
231+
232+
try {
233+
const list = await R2_BUCKET.list({ limit: 1000 });
234+
235+
const files = list.objects.map(obj => ({
236+
name: obj.key,
237+
size: obj.size,
238+
uploadedAt: obj.uploaded,
239+
}));
240+
241+
return new Response(JSON.stringify({ files }, null, 2), {
242+
headers: { "Content-Type": "application/json", ...corsHeaders },
243+
});
244+
} catch (error) {
245+
return new Response(JSON.stringify({ error: error.message }), {
246+
status: 500,
247+
headers: { "Content-Type": "application/json" },
248+
});
249+
}
250+
}`;
251+
252+
// Write wrangler.toml
253+
const tempDir = require("os").tmpdir();
254+
const workerDir = path.join(tempDir, "quick-share-worker");
255+
execSync(`rm -rf "${workerDir}" && mkdir -p "${workerDir}"`);
256+
257+
const wranglerToml = `
258+
name = "${options.name}"
259+
main = "index.js"
260+
compatibility_date = "2023-12-01"
261+
262+
[[r2_buckets]]
263+
binding = "R2_BUCKET"
264+
bucket_name = "${config.bucketName}"
265+
`;
266+
267+
fs.writeFileSync(path.join(workerDir, "wrangler.toml"), wranglerToml);
268+
fs.writeFileSync(path.join(workerDir, "index.js"), workerContent);
269+
270+
console.log(chalk.blue("Deploying worker..."));
271+
execSync(`cd "${workerDir}" && wrangler deploy`, { stdio: "inherit" });
272+
273+
console.log(chalk.green("\n✓ Worker deployed!"));
274+
console.log(chalk.blue("\nWorker URL:"));
275+
console.log(
276+
chalk.white(
277+
` https://${options.name}.${config.accountId}.workers.dev/files`,
278+
),
279+
);
280+
281+
// Cleanup
282+
execSync(`rm -rf "${workerDir}"`);
283+
284+
console.log(chalk.blue("\nNow update your library:"));
285+
console.log(chalk.white(` quick-share library`));
286+
});
287+
110288
// Default action - if just a file path is provided
111289
program
112290
.arguments("<file>")

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"bin/",
5858
"scripts/",
5959
"lib/",
60+
"assets/",
6061
"README.md",
6162
"LICENSE"
6263
]

0 commit comments

Comments
 (0)