源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { basename } from "node:path";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
|
||||
const usage = "Usage: node .opencode/skills/upload-photo/scripts/upload.mjs <file.png> [more files...] [--prefix <path/prefix>] [--stable]";
|
||||
|
||||
function printUsage(message = null) {
|
||||
if (message) console.error(message);
|
||||
console.error(usage);
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const files = [];
|
||||
let prefix = `uploads/${new Date().toISOString().slice(0, 10)}`;
|
||||
let stable = false;
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--stable") {
|
||||
stable = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--prefix") {
|
||||
const value = argv[index + 1];
|
||||
if (!value || value.startsWith("--")) return null;
|
||||
prefix = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("--")) return null;
|
||||
files.push(arg);
|
||||
}
|
||||
|
||||
if (files.length === 0) return null;
|
||||
return { files, prefix, stable };
|
||||
}
|
||||
|
||||
function contentTypeFor(file) {
|
||||
const lower = file.toLowerCase();
|
||||
if (lower.endsWith(".png")) return "image/png";
|
||||
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
||||
if (lower.endsWith(".gif")) return "image/gif";
|
||||
if (lower.endsWith(".webp")) return "image/webp";
|
||||
if (lower.endsWith(".svg")) return "image/svg+xml";
|
||||
if (lower.endsWith(".avif")) return "image/avif";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
function encodePathname(pathname) {
|
||||
return pathname.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
||||
}
|
||||
|
||||
function buildPathname(prefix, file) {
|
||||
const cleanPrefix = prefix.replace(/^\/+|\/+$/g, "");
|
||||
return encodePathname([cleanPrefix, basename(file)].filter(Boolean).join("/"));
|
||||
}
|
||||
|
||||
async function uploadFile({ file, prefix, stable, token }) {
|
||||
if (!existsSync(file)) {
|
||||
console.error(`File does not exist: ${file}`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const pathname = buildPathname(prefix, file);
|
||||
const headers = {
|
||||
authorization: `Bearer ${token}`,
|
||||
"x-content-type": contentTypeFor(file),
|
||||
};
|
||||
if (stable) headers["x-add-random-suffix"] = "0";
|
||||
|
||||
const response = await fetch(`https://blob.vercel-storage.com/${pathname}`, {
|
||||
method: "PUT",
|
||||
headers,
|
||||
body: readFileSync(file),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const detail = (await response.text()).slice(0, 300);
|
||||
console.error(`Upload failed (${response.status}) for ${file}: ${detail}`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let payload;
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch {
|
||||
console.error(`Upload failed for ${file}: response was not JSON`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!payload || typeof payload.url !== "string" || payload.url.length === 0) {
|
||||
console.error(`Upload failed for ${file}: response did not include url`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
console.log(payload.url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
if (!args) {
|
||||
printUsage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const token = process.env.BLOB_READ_WRITE_TOKEN;
|
||||
if (!token) {
|
||||
console.error("BLOB_READ_WRITE_TOKEN is not set — fetch it with the get-env-var skill: export BLOB_READ_WRITE_TOKEN=\"$(infisical secrets get BLOB_READ_WRITE_TOKEN --plain --silent)\"");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const file of args.files) {
|
||||
const status = await uploadFile({ file, prefix: args.prefix, stable: args.stable, token });
|
||||
if (status !== 0) return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
process.exitCode = await main();
|
||||
Reference in New Issue
Block a user