mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 17:54:23 +01:00
Improve http error codes (#3186)
This commit is contained in:
@@ -33,6 +33,8 @@ import { lazy } from "../util/lazy"
|
|||||||
import { Todo } from "../session/todo"
|
import { Todo } from "../session/todo"
|
||||||
import { InstanceBootstrap } from "../project/bootstrap"
|
import { InstanceBootstrap } from "../project/bootstrap"
|
||||||
import { MCP } from "../mcp"
|
import { MCP } from "../mcp"
|
||||||
|
import { Storage } from "../storage/storage"
|
||||||
|
import type { ContentfulStatusCode } from "hono/utils/http-status"
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
400: {
|
400: {
|
||||||
@@ -42,17 +44,33 @@ const ERRORS = {
|
|||||||
schema: resolver(
|
schema: resolver(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
data: z.record(z.string(), z.any()),
|
data: z.any().nullable(),
|
||||||
|
errors: z.array(z.record(z.string(), z.any())),
|
||||||
|
success: z.literal(false),
|
||||||
})
|
})
|
||||||
.meta({
|
.meta({
|
||||||
ref: "Error",
|
ref: "BadRequestError",
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
404: {
|
||||||
|
description: "Not found",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
Storage.NotFoundError.Schema
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
function errors(...codes: number[]) {
|
||||||
|
return Object.fromEntries(codes.map((code) => [code, ERRORS[code as keyof typeof ERRORS]]))
|
||||||
|
}
|
||||||
|
|
||||||
export namespace Server {
|
export namespace Server {
|
||||||
const log = Log.create({ service: "server" })
|
const log = Log.create({ service: "server" })
|
||||||
|
|
||||||
@@ -68,13 +86,18 @@ export namespace Server {
|
|||||||
error: err,
|
error: err,
|
||||||
})
|
})
|
||||||
if (err instanceof NamedError) {
|
if (err instanceof NamedError) {
|
||||||
return c.json(err.toObject(), {
|
let status: ContentfulStatusCode
|
||||||
status: 400,
|
if (err instanceof Storage.NotFoundError)
|
||||||
})
|
status = 404
|
||||||
|
else if (err instanceof Provider.ModelNotFoundError)
|
||||||
|
status = 400
|
||||||
|
else
|
||||||
|
status = 500
|
||||||
|
return c.json(err.toObject(), { status })
|
||||||
}
|
}
|
||||||
const message = err instanceof Error && err.stack ? err.stack : err.toString()
|
const message = err instanceof Error && err.stack ? err.stack : err.toString()
|
||||||
return c.json(new NamedError.Unknown({ message }).toObject(), {
|
return c.json(new NamedError.Unknown({ message }).toObject(), {
|
||||||
status: 400,
|
status: 500,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.use(async (c, next) => {
|
.use(async (c, next) => {
|
||||||
@@ -153,7 +176,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...ERRORS,
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator("json", Config.Info),
|
validator("json", Config.Info),
|
||||||
@@ -177,7 +200,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...ERRORS,
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
@@ -210,7 +233,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...ERRORS,
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -305,6 +328,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -333,6 +357,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -361,6 +386,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -381,7 +407,7 @@ export namespace Server {
|
|||||||
description: "Create a new session",
|
description: "Create a new session",
|
||||||
operationId: "session.create",
|
operationId: "session.create",
|
||||||
responses: {
|
responses: {
|
||||||
...ERRORS,
|
...errors(400),
|
||||||
200: {
|
200: {
|
||||||
description: "Successfully created session",
|
description: "Successfully created session",
|
||||||
content: {
|
content: {
|
||||||
@@ -413,6 +439,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -440,6 +467,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -481,6 +509,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -541,6 +570,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -567,6 +597,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -596,6 +627,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -625,6 +657,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -661,6 +694,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -693,6 +727,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -727,6 +762,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -762,6 +798,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -792,6 +829,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -822,6 +860,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -852,6 +891,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -879,6 +919,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400, 404),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -1132,6 +1173,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -1223,6 +1265,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -1355,6 +1398,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
@@ -1406,7 +1450,7 @@ export namespace Server {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
...ERRORS,
|
...errors(400),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
validator(
|
validator(
|
||||||
|
|||||||
@@ -5,12 +5,21 @@ import { Global } from "../global"
|
|||||||
import { lazy } from "../util/lazy"
|
import { lazy } from "../util/lazy"
|
||||||
import { Lock } from "../util/lock"
|
import { Lock } from "../util/lock"
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
|
import { NamedError } from "@/util/error"
|
||||||
|
import z from "zod"
|
||||||
|
|
||||||
export namespace Storage {
|
export namespace Storage {
|
||||||
const log = Log.create({ service: "storage" })
|
const log = Log.create({ service: "storage" })
|
||||||
|
|
||||||
type Migration = (dir: string) => Promise<void>
|
type Migration = (dir: string) => Promise<void>
|
||||||
|
|
||||||
|
export const NotFoundError = NamedError.create(
|
||||||
|
"NotFoundError",
|
||||||
|
z.object({
|
||||||
|
message: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const MIGRATIONS: Migration[] = [
|
const MIGRATIONS: Migration[] = [
|
||||||
async (dir) => {
|
async (dir) => {
|
||||||
const project = path.resolve(dir, "../project")
|
const project = path.resolve(dir, "../project")
|
||||||
@@ -131,31 +140,51 @@ export namespace Storage {
|
|||||||
export async function remove(key: string[]) {
|
export async function remove(key: string[]) {
|
||||||
const dir = await state().then((x) => x.dir)
|
const dir = await state().then((x) => x.dir)
|
||||||
const target = path.join(dir, ...key) + ".json"
|
const target = path.join(dir, ...key) + ".json"
|
||||||
|
return withErrorHandling(async () => {
|
||||||
await fs.unlink(target).catch(() => {})
|
await fs.unlink(target).catch(() => {})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function read<T>(key: string[]) {
|
export async function read<T>(key: string[]) {
|
||||||
const dir = await state().then((x) => x.dir)
|
const dir = await state().then((x) => x.dir)
|
||||||
const target = path.join(dir, ...key) + ".json"
|
const target = path.join(dir, ...key) + ".json"
|
||||||
|
return withErrorHandling(async () => {
|
||||||
using _ = await Lock.read(target)
|
using _ = await Lock.read(target)
|
||||||
return Bun.file(target).json() as Promise<T>
|
return Bun.file(target).json() as Promise<T>
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update<T>(key: string[], fn: (draft: T) => void) {
|
export async function update<T>(key: string[], fn: (draft: T) => void) {
|
||||||
const dir = await state().then((x) => x.dir)
|
const dir = await state().then((x) => x.dir)
|
||||||
const target = path.join(dir, ...key) + ".json"
|
const target = path.join(dir, ...key) + ".json"
|
||||||
|
return withErrorHandling(async () => {
|
||||||
using _ = await Lock.write("storage")
|
using _ = await Lock.write("storage")
|
||||||
const content = await Bun.file(target).json()
|
const content = await Bun.file(target).json()
|
||||||
fn(content)
|
fn(content)
|
||||||
await Bun.write(target, JSON.stringify(content, null, 2))
|
await Bun.write(target, JSON.stringify(content, null, 2))
|
||||||
return content as T
|
return content as T
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function write<T>(key: string[], content: T) {
|
export async function write<T>(key: string[], content: T) {
|
||||||
const dir = await state().then((x) => x.dir)
|
const dir = await state().then((x) => x.dir)
|
||||||
const target = path.join(dir, ...key) + ".json"
|
const target = path.join(dir, ...key) + ".json"
|
||||||
|
return withErrorHandling(async () => {
|
||||||
using _ = await Lock.write("storage")
|
using _ = await Lock.write("storage")
|
||||||
await Bun.write(target, JSON.stringify(content, null, 2))
|
await Bun.write(target, JSON.stringify(content, null, 2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withErrorHandling<T>(body: () => Promise<T>) {
|
||||||
|
return body().catch((e) => {
|
||||||
|
if (!(e instanceof Error))
|
||||||
|
throw e
|
||||||
|
const errnoException = e as NodeJS.ErrnoException
|
||||||
|
if (errnoException.code === "ENOENT") {
|
||||||
|
throw new NotFoundError({ message: `Resource not found: ${errnoException.path}` })
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const glob = new Bun.Glob("**/*")
|
const glob = new Bun.Glob("**/*")
|
||||||
|
|||||||
Reference in New Issue
Block a user