Initial commit (by create-cloudflare CLI)
Details: C3 = create-cloudflare@2.49.0 project name = bluesky-alt-text-worker package manager = npm@10.2.3 wrangler = wrangler@4.19.1 git = 2.48.1
This commit is contained in:
commit
bbc7b5bf9b
14 changed files with 7899 additions and 0 deletions
172
.gitignore
vendored
Normal file
172
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
\*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
\*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
\*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
\*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
.cache/
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.\*
|
||||
|
||||
# wrangler project
|
||||
|
||||
.dev.vars
|
||||
.wrangler/
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"wrangler.json": "jsonc"
|
||||
}
|
||||
}
|
||||
25
README.md
Normal file
25
README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Cloudflare Workers OpenAPI 3.1
|
||||
|
||||
This is a Cloudflare Worker with OpenAPI 3.1 using [chanfana](https://github.com/cloudflare/chanfana) and [Hono](https://github.com/honojs/hono).
|
||||
|
||||
This is an example project made to be used as a quick start into building OpenAPI compliant Workers that generates the
|
||||
`openapi.json` schema automatically from code and validates the incoming request to the defined parameters or request body.
|
||||
|
||||
## Get started
|
||||
|
||||
1. Sign up for [Cloudflare Workers](https://workers.dev). The free tier is more than enough for most use cases.
|
||||
2. Clone this project and install dependencies with `npm install`
|
||||
3. Run `wrangler login` to login to your Cloudflare account in wrangler
|
||||
4. Run `wrangler deploy` to publish the API to Cloudflare Workers
|
||||
|
||||
## Project structure
|
||||
|
||||
1. Your main router is defined in `src/index.ts`.
|
||||
2. Each endpoint has its own file in `src/endpoints/`.
|
||||
3. For more information read the [chanfana documentation](https://chanfana.pages.dev/) and [Hono documentation](https://hono.dev/docs).
|
||||
|
||||
## Development
|
||||
|
||||
1. Run `wrangler dev` to start a local instance of the API.
|
||||
2. Open `http://localhost:8787/` in your browser to see the Swagger interface where you can try the endpoints.
|
||||
3. Changes made in the `src/` folder will automatically trigger the server to reload, you only need to refresh the Swagger interface.
|
||||
1541
package-lock.json
generated
Normal file
1541
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "cloudflare-workers-openapi",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"deploy": "wrangler deploy",
|
||||
"dev": "wrangler dev",
|
||||
"start": "wrangler dev",
|
||||
"cf-typegen": "wrangler types"
|
||||
},
|
||||
"dependencies": {
|
||||
"chanfana": "^2.6.3",
|
||||
"hono": "^4.6.20",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.13.0",
|
||||
"@types/service-worker-mock": "^2.0.4",
|
||||
"wrangler": "^4.19.1"
|
||||
}
|
||||
}
|
||||
58
src/endpoints/taskCreate.ts
Normal file
58
src/endpoints/taskCreate.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { Bool, OpenAPIRoute } from "chanfana";
|
||||
import { z } from "zod";
|
||||
import { type AppContext, Task } from "../types";
|
||||
|
||||
export class TaskCreate extends OpenAPIRoute {
|
||||
schema = {
|
||||
tags: ["Tasks"],
|
||||
summary: "Create a new Task",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: Task,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Returns the created task",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
series: z.object({
|
||||
success: Bool(),
|
||||
result: z.object({
|
||||
task: Task,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async handle(c: AppContext) {
|
||||
// Get validated data
|
||||
const data = await this.getValidatedData<typeof this.schema>();
|
||||
|
||||
// Retrieve the validated request body
|
||||
const taskToCreate = data.body;
|
||||
|
||||
// Implement your own object insertion here
|
||||
|
||||
// return the new task
|
||||
return {
|
||||
success: true,
|
||||
task: {
|
||||
name: taskToCreate.name,
|
||||
slug: taskToCreate.slug,
|
||||
description: taskToCreate.description,
|
||||
completed: taskToCreate.completed,
|
||||
due_date: taskToCreate.due_date,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
56
src/endpoints/taskDelete.ts
Normal file
56
src/endpoints/taskDelete.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { Bool, OpenAPIRoute, Str } from "chanfana";
|
||||
import { z } from "zod";
|
||||
import { type AppContext, Task } from "../types";
|
||||
|
||||
export class TaskDelete extends OpenAPIRoute {
|
||||
schema = {
|
||||
tags: ["Tasks"],
|
||||
summary: "Delete a Task",
|
||||
request: {
|
||||
params: z.object({
|
||||
taskSlug: Str({ description: "Task slug" }),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Returns if the task was deleted successfully",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
series: z.object({
|
||||
success: Bool(),
|
||||
result: z.object({
|
||||
task: Task,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async handle(c: AppContext) {
|
||||
// Get validated data
|
||||
const data = await this.getValidatedData<typeof this.schema>();
|
||||
|
||||
// Retrieve the validated slug
|
||||
const { taskSlug } = data.params;
|
||||
|
||||
// Implement your own object deletion here
|
||||
|
||||
// Return the deleted task for confirmation
|
||||
return {
|
||||
result: {
|
||||
task: {
|
||||
name: "Build something awesome with Cloudflare Workers",
|
||||
slug: taskSlug,
|
||||
description: "Lorem Ipsum",
|
||||
completed: true,
|
||||
due_date: "2022-12-24",
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
81
src/endpoints/taskFetch.ts
Normal file
81
src/endpoints/taskFetch.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { Bool, OpenAPIRoute, Str } from "chanfana";
|
||||
import { z } from "zod";
|
||||
import { type AppContext, Task } from "../types";
|
||||
|
||||
export class TaskFetch extends OpenAPIRoute {
|
||||
schema = {
|
||||
tags: ["Tasks"],
|
||||
summary: "Get a single Task by slug",
|
||||
request: {
|
||||
params: z.object({
|
||||
taskSlug: Str({ description: "Task slug" }),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Returns a single task if found",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
series: z.object({
|
||||
success: Bool(),
|
||||
result: z.object({
|
||||
task: Task,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
"404": {
|
||||
description: "Task not found",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
series: z.object({
|
||||
success: Bool(),
|
||||
error: Str(),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async handle(c: AppContext) {
|
||||
// Get validated data
|
||||
const data = await this.getValidatedData<typeof this.schema>();
|
||||
|
||||
// Retrieve the validated slug
|
||||
const { taskSlug } = data.params;
|
||||
|
||||
// Implement your own object fetch here
|
||||
|
||||
const exists = true;
|
||||
|
||||
// @ts-ignore: check if the object exists
|
||||
if (exists === false) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Object not found",
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
task: {
|
||||
name: "my task",
|
||||
slug: taskSlug,
|
||||
description: "this needs to be done",
|
||||
completed: false,
|
||||
due_date: new Date().toISOString().slice(0, 10),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
69
src/endpoints/taskList.ts
Normal file
69
src/endpoints/taskList.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { Bool, Num, OpenAPIRoute } from "chanfana";
|
||||
import { z } from "zod";
|
||||
import { type AppContext, Task } from "../types";
|
||||
|
||||
export class TaskList extends OpenAPIRoute {
|
||||
schema = {
|
||||
tags: ["Tasks"],
|
||||
summary: "List Tasks",
|
||||
request: {
|
||||
query: z.object({
|
||||
page: Num({
|
||||
description: "Page number",
|
||||
default: 0,
|
||||
}),
|
||||
isCompleted: Bool({
|
||||
description: "Filter by completed flag",
|
||||
required: false,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Returns a list of tasks",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
series: z.object({
|
||||
success: Bool(),
|
||||
result: z.object({
|
||||
tasks: Task.array(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async handle(c: AppContext) {
|
||||
// Get validated data
|
||||
const data = await this.getValidatedData<typeof this.schema>();
|
||||
|
||||
// Retrieve the validated parameters
|
||||
const { page, isCompleted } = data.query;
|
||||
|
||||
// Implement your own object list here
|
||||
|
||||
return {
|
||||
success: true,
|
||||
tasks: [
|
||||
{
|
||||
name: "Clean my room",
|
||||
slug: "clean-room",
|
||||
description: null,
|
||||
completed: false,
|
||||
due_date: "2025-01-05",
|
||||
},
|
||||
{
|
||||
name: "Build something awesome with Cloudflare Workers",
|
||||
slug: "cloudflare-workers",
|
||||
description: "Lorem Ipsum",
|
||||
completed: true,
|
||||
due_date: "2022-12-24",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
26
src/index.ts
Normal file
26
src/index.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { fromHono } from "chanfana";
|
||||
import { Hono } from "hono";
|
||||
import { TaskCreate } from "./endpoints/taskCreate";
|
||||
import { TaskDelete } from "./endpoints/taskDelete";
|
||||
import { TaskFetch } from "./endpoints/taskFetch";
|
||||
import { TaskList } from "./endpoints/taskList";
|
||||
|
||||
// Start a Hono app
|
||||
const app = new Hono<{ Bindings: Env }>();
|
||||
|
||||
// Setup OpenAPI registry
|
||||
const openapi = fromHono(app, {
|
||||
docs_url: "/",
|
||||
});
|
||||
|
||||
// Register OpenAPI endpoints
|
||||
openapi.get("/api/tasks", TaskList);
|
||||
openapi.post("/api/tasks", TaskCreate);
|
||||
openapi.get("/api/tasks/:taskSlug", TaskFetch);
|
||||
openapi.delete("/api/tasks/:taskSlug", TaskDelete);
|
||||
|
||||
// You may also register routes for non OpenAPI directly on Hono
|
||||
// app.get('/test', (c) => c.text('Hono!'))
|
||||
|
||||
// Export the Hono app
|
||||
export default app;
|
||||
13
src/types.ts
Normal file
13
src/types.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { DateTime, Str } from "chanfana";
|
||||
import type { Context } from "hono";
|
||||
import { z } from "zod";
|
||||
|
||||
export type AppContext = Context<{ Bindings: Env }>;
|
||||
|
||||
export const Task = z.object({
|
||||
name: Str({ example: "lorem" }),
|
||||
slug: Str(),
|
||||
description: Str({ required: false }),
|
||||
completed: z.boolean().default(false),
|
||||
due_date: DateTime(),
|
||||
});
|
||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Base Options: */
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2022",
|
||||
"verbatimModuleSyntax": false,
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleDetection": "force",
|
||||
/* Strictness */
|
||||
"noImplicitAny": false,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": false,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
/* If NOT transpiling with TypeScript: */
|
||||
"moduleResolution": "Bundler",
|
||||
"module": "es2022",
|
||||
"noEmit": true,
|
||||
"lib": ["es2022"],
|
||||
"types": [
|
||||
"./worker-configuration.d.ts",
|
||||
"@types/node",
|
||||
"@types/service-worker-mock"
|
||||
]
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "tests"],
|
||||
"include": ["src", "worker-configuration.d.ts"]
|
||||
}
|
||||
5755
worker-configuration.d.ts
vendored
Normal file
5755
worker-configuration.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
47
wrangler.jsonc
Normal file
47
wrangler.jsonc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* For more details on how to configure Wrangler, refer to:
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||
*/
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "bluesky-alt-text-worker",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2025-06-07",
|
||||
"observability": {
|
||||
"enabled": true
|
||||
}
|
||||
/**
|
||||
* Smart Placement
|
||||
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
|
||||
*/
|
||||
// "placement": { "mode": "smart" },
|
||||
|
||||
/**
|
||||
* Bindings
|
||||
* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
|
||||
* databases, object storage, AI inference, real-time communication and more.
|
||||
* https://developers.cloudflare.com/workers/runtime-apis/bindings/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Environment Variables
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
|
||||
*/
|
||||
// "vars": { "MY_VARIABLE": "production_value" },
|
||||
/**
|
||||
* Note: Use secrets to store sensitive data.
|
||||
* https://developers.cloudflare.com/workers/configuration/secrets/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static Assets
|
||||
* https://developers.cloudflare.com/workers/static-assets/binding/
|
||||
*/
|
||||
// "assets": { "directory": "./public/", "binding": "ASSETS" },
|
||||
|
||||
/**
|
||||
* Service Bindings (communicate between multiple Workers)
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
|
||||
*/
|
||||
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
|
||||
}
|
||||
Loading…
Reference in a new issue