feat: cleanup, status codes, Bearer auth, env vars

This commit is contained in:
Index 2025-06-11 07:15:05 -05:00
parent 0c09378dff
commit d8b1870059
7 changed files with 73 additions and 55 deletions

View file

@ -3,3 +3,5 @@
A Cloudflare Worker which works in conjunction with https://github.com/indexxing/Bluesky-Alt-Text. All endpoints are based off the original cloud function made by [symmetricalboy](https://github.com/symmetricalboy) [here](https://github.com/symmetricalboy/gen-alt-text/blob/main/functions/index.js), just in Typescript and prepackaged into a Cloudflare Worker environment.
Documentation is served at the root of the worker deployment. There is a root path variable specified in the entrypoint file because my setup involves using a worker route wildcard on my custom domain.
**Note:** This does not support video captioning, or uploading larger media for processing yet like the source cloud function.

View file

@ -8,7 +8,7 @@ export class CondenseTextEndpoint extends OpenAPIRoute {
summary: "Condense a given text based on a directive",
security: [
{
apiKey: [],
bearerAuth: [],
},
],
request: {
@ -67,8 +67,7 @@ export class CondenseTextEndpoint extends OpenAPIRoute {
try {
const res = await c.var.gemini.models.generateContent({
// * Original cloud function used "gemini-2.0-flash", but I think the lite version should work fine too.
model: "gemini-2.0-flash-lite",
model: c.env.GEMINI_MODEL,
contents: [{
parts: [
{
@ -79,14 +78,16 @@ export class CondenseTextEndpoint extends OpenAPIRoute {
],
}],
config: {
temperature: 0.2,
maxOutputTokens: 1024,
temperature: c.env.GEMINI_CONDENSE_TEMPERATURE,
maxOutputTokens: c.env.GEMINI_CONDENSE_MAX_OUTPUT_TOKENS,
},
});
const condensedText = res.candidates?.[0]?.content?.parts?.[0]
?.text;
if (!condensedText) {
c.status(502); // Bad response from upstream API resulting in "Bad Gateway" status
return {
success: false,
error: "Failed to condense text.",
@ -95,12 +96,15 @@ export class CondenseTextEndpoint extends OpenAPIRoute {
return {
success: true,
altText: condensedText,
text: condensedText,
tokens: res.usageMetadata.totalTokenCount ?? 0,
};
} catch (e) {
c.status(500);
return {
success: false,
message: e,
error: e,
};
}
}

View file

@ -52,7 +52,7 @@ export class GenerateEndpoint extends OpenAPIRoute {
summary: "Generates alt text for a given image.",
security: [
{
apiKey: [],
bearerAuth: [],
},
],
request: {
@ -145,17 +145,16 @@ export class GenerateEndpoint extends OpenAPIRoute {
try {
const res = await c.var.gemini.models.generateContent({
// * Original cloud function used "gemini-2.0-flash", but I think the lite version should work fine too.
model: "gemini-2.0-flash-lite",
model: c.env.GEMINI_MODEL,
contents: [
{ text: systemInstructions },
{ inlineData: { mimeType: mimeType, data: base64Data } },
],
config: {
temperature: 0.2, // Lower temperature for more deterministic output
maxOutputTokens: 2048, // Allow for longer descriptions if needed
topP: 0.95,
topK: 64,
temperature: c.env.GEMINI_GENERATE_TEMPERATURE,
maxOutputTokens: c.env.GEMINI_GENERATE_MAX_OUTPUT_TOKENS,
topP: c.env.GEMINI_GENERATE_TOP_P,
topK: c.env.GEMINI_GENERATE_TOP_K,
},
});
@ -170,12 +169,13 @@ export class GenerateEndpoint extends OpenAPIRoute {
return {
success: true,
altText: generatedText,
text: generatedText,
tokens: res.usageMetadata.totalTokenCount ?? 0,
};
} catch (e) {
return {
success: false,
message: e,
error: e,
};
}
}

View file

@ -16,21 +16,12 @@ const rootPath = "/api/altText/";
app.use(
"*",
cors({
origin: (origin) => {
const allowedOrigins = [
"https://indexx.dev",
"moz-extension://",
"chrome-extension://",
];
if (
origin &&
allowedOrigins.some((allowed) => origin.startsWith(allowed))
) {
return origin;
}
return null;
},
origin: [
"https://indexx.dev",
"chrome-extension://",
"safari-web-extension://",
"moz-extension://",
],
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: ["*"],
maxAge: 600,
@ -51,10 +42,10 @@ const openapi = fromHono(app, {
openapi_url: rootPath + "openapi.json",
});
openapi.registry.registerComponent("securitySchemes", "apiKey", {
type: "apiKey",
name: "Authorization",
in: "header",
openapi.registry.registerComponent("securitySchemes", "bearerAuth", {
type: "http",
scheme: "bearer",
bearerFormat: "API key",
});
// Define Middlewares

View file

@ -5,22 +5,35 @@ export async function authMiddleware(
c: Context<{ Bindings: Env; Variables: Variables }>,
next: Next,
) {
const authToken = c.req.header("Authorization");
if (!authToken) {
c.status(401);
if (!c.env.AUTH_TOKEN) {
return c.json({
success: false,
error: "No authentication token provided.",
});
error: "Authentication token is not specified in worker secrets.",
}, 500);
}
if (authToken !== c.env.AUTH_TOKEN) {
c.status(403);
const authToken = c.req.header("Authorization");
if (!authToken || !authToken.startsWith("Bearer ")) {
return c.json(
{
success: false,
error:
"Authentication token missing or malformed. Expected 'Bearer <token>'.",
},
401,
{
"WWW-Authenticate": "Bearer",
},
);
}
const token = authToken.split(" ")[1];
if (token !== c.env.AUTH_TOKEN) {
return c.json({
success: false,
error: "Authentication token provided is invalid.",
});
}, 401);
}
await next();

View file

@ -1,6 +1,4 @@
import { DateTime, Str } from "chanfana";
import type { Context } from "hono";
import { z } from "zod";
import type { GoogleGenAI } from "@google/genai";
export type AppContext = Context<{ Bindings: Env; Variables: Variables }>;
@ -11,6 +9,15 @@ export interface Env {
ABSOLUTE_MAX_LENGTH: number;
MAX_DIRECT_BLOB_SIZE: number;
GEMINI_MODEL: string;
GEMINI_GENERATE_TEMPERATURE: number;
GEMINI_GENERATE_MAX_OUTPUT_TOKENS: number;
GEMINI_GENERATE_TOP_P: number;
GEMINI_GENERATE_TOP_K: number;
GEMINI_CONDENSE_TEMPERATURE: number;
GEMINI_CONDENSE_MAX_OUTPUT_TOKENS: number;
// Secrets
AUTH_TOKEN: string;
GEMINI_API_KEY: string;
@ -19,11 +26,3 @@ export interface Env {
export type Variables = {
gemini: GoogleGenAI;
};
export const Task = z.object({
name: Str({ example: "lorem" }),
slug: Str(),
description: Str({ required: false }),
completed: z.boolean().default(false),
due_date: DateTime(),
});

View file

@ -12,6 +12,15 @@
"vars": {
"MAX_ALT_TEXT_LENGTH": 2000,
"ABSOLUTE_MAX_LENGTH": 5000,
"MAX_DIRECT_BLOB_SIZE": 5242880
"MAX_DIRECT_BLOB_SIZE": 5242880,
"GEMINI_MODEL": "gemini-2.0-flash-lite",
"GEMINI_GENERATE_TEMPERATURE": 0.2,
"GEMINI_GENERATE_MAX_OUTPUT_TOKENS": 2048,
"GEMINI_GENERATE_TOP_P": 0.95,
"GEMINI_GENERATE_TOP_K": 64,
"GEMINI_CONDENSE_TEMPERATURE": 0.2,
"GEMINI_CONDENSE_MAX_OUTPUT_TOKENS": 1024
}
}