feat: move base URL, API key, and model to server .env

- BASE_URL, API_KEY, MODEL now read from process.env (Bun auto-loads .env)
- requireEnv() fails fast at startup if any is missing
- request body simplifies to { prompt, size, referenceImages? }
- client drops the three fields from form and localStorage
- add .env.example as the variable-name source of truth
- AGENTS.md notes the 0.0.0.0 bind now exposes the upstream quota to
  anyone reachable on the network
This commit is contained in:
2026-05-18 23:28:12 +08:00
parent 4ef2f1ba2b
commit d5bbc14c8d
5 changed files with 64 additions and 62 deletions
+21 -21
View File
@@ -6,14 +6,23 @@ import index from "./index.html";
type Size = `${number}x${number}`;
type GenerateRequest = {
baseURL?: string;
apiKey?: string;
model?: string;
prompt?: string;
size?: Size;
referenceImages?: string[];
};
function requireEnv(name: string): string {
const v = process.env[name];
if (!v) {
throw new Error(`Missing required env: ${name} (see .env.example).`);
}
return v;
}
const BASE_URL = requireEnv("BASE_URL");
const API_KEY = requireEnv("API_KEY");
const MODEL = requireEnv("MODEL");
function decodeDataUrl(
dataUrl: string,
): { bytes: Uint8Array<ArrayBuffer>; mime: string } | null {
@@ -27,22 +36,19 @@ function decodeDataUrl(
}
async function callUpstream(args: {
baseURL: string;
apiKey: string;
model: string;
prompt: string;
size: Size;
referenceImages: string[];
stream: boolean;
signal?: AbortSignal;
}): Promise<Response> {
const { baseURL, apiKey, model, prompt, size, referenceImages, stream, signal } = args;
const { prompt, size, referenceImages, stream, signal } = args;
const isEdit = referenceImages.length > 0;
const url = `${baseURL.replace(/\/+$/, "")}/images/${isEdit ? "edits" : "generations"}`;
const url = `${BASE_URL.replace(/\/+$/, "")}/images/${isEdit ? "edits" : "generations"}`;
if (isEdit) {
const form = new FormData();
form.append("model", model);
form.append("model", MODEL);
form.append("prompt", prompt);
form.append("size", size);
if (stream) {
@@ -64,13 +70,13 @@ async function callUpstream(args: {
}
return fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
signal,
});
}
const body: Record<string, unknown> = { model, prompt, size };
const body: Record<string, unknown> = { model: MODEL, prompt, size };
if (stream) {
body.stream = true;
body.partial_images = 2;
@@ -78,7 +84,7 @@ async function callUpstream(args: {
return fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
@@ -180,18 +186,12 @@ const app = new Hono();
app.post("/api/generate", async (c) => {
const body = (await c.req.json()) as GenerateRequest;
const { baseURL, apiKey, model, prompt, size, referenceImages } = body;
if (!baseURL || !apiKey || !model || !prompt) {
return c.json(
{ error: "baseURL, apiKey, model, prompt are required" },
400,
);
const { prompt, size, referenceImages } = body;
if (!prompt) {
return c.json({ error: "prompt is required" }, 400);
}
const refs = Array.isArray(referenceImages) ? referenceImages : [];
const args = {
baseURL,
apiKey,
model,
prompt,
size: size ?? ("1024x1024" as Size),
referenceImages: refs,