feat: cleanup, status codes, Bearer auth, env vars
This commit is contained in:
parent
0c09378dff
commit
d8b1870059
7 changed files with 73 additions and 55 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/index.ts
25
src/index.ts
|
|
@ -16,21 +16,12 @@ const rootPath = "/api/altText/";
|
|||
app.use(
|
||||
"*",
|
||||
cors({
|
||||
origin: (origin) => {
|
||||
const allowedOrigins = [
|
||||
origin: [
|
||||
"https://indexx.dev",
|
||||
"moz-extension://",
|
||||
"chrome-extension://",
|
||||
];
|
||||
|
||||
if (
|
||||
origin &&
|
||||
allowedOrigins.some((allowed) => origin.startsWith(allowed))
|
||||
) {
|
||||
return origin;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
"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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
19
src/types.ts
19
src/types.ts
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue