## Summary - add server-backed speech capabilities and transcription endpoints plus UI settings for speech configuration - add push-to-talk prompt voice input with microphone controls, transcription insertion, and browser capability gating - keep prompt controls aligned by restoring right-side nav placement and moving the mic beside the expand control
85 lines
3.1 KiB
TypeScript
85 lines
3.1 KiB
TypeScript
import { FastifyInstance } from "fastify"
|
|
import { z } from "zod"
|
|
import { probeBinaryVersion } from "../../workspaces/runtime"
|
|
import type { SettingsService } from "../../settings/service"
|
|
import type { Logger } from "../../logger"
|
|
import { sanitizeConfigDoc, sanitizeConfigOwner } from "../../settings/public-config"
|
|
|
|
interface RouteDeps {
|
|
settings: SettingsService
|
|
logger: Logger
|
|
}
|
|
|
|
const ValidateBinarySchema = z.object({
|
|
path: z.string(),
|
|
})
|
|
|
|
function validateBinaryPath(binaryPath: string): { valid: boolean; version?: string; error?: string } {
|
|
const result = probeBinaryVersion(binaryPath)
|
|
return { valid: result.valid, version: result.version, error: result.error }
|
|
}
|
|
|
|
export function registerSettingsRoutes(app: FastifyInstance, deps: RouteDeps) {
|
|
// Full-document access
|
|
app.get("/api/storage/config", async () => sanitizeConfigDoc(deps.settings.getDoc("config")))
|
|
app.patch("/api/storage/config", async (request, reply) => {
|
|
try {
|
|
return sanitizeConfigDoc(deps.settings.mergePatchDoc("config", request.body ?? {}))
|
|
} catch (error) {
|
|
reply.code(400)
|
|
return { error: error instanceof Error ? error.message : "Invalid patch" }
|
|
}
|
|
})
|
|
|
|
app.get<{ Params: { owner: string } }>("/api/storage/config/:owner", async (request) => {
|
|
return sanitizeConfigOwner(request.params.owner, deps.settings.getOwner("config", request.params.owner))
|
|
})
|
|
|
|
app.patch<{ Params: { owner: string } }>("/api/storage/config/:owner", async (request, reply) => {
|
|
try {
|
|
return sanitizeConfigOwner(
|
|
request.params.owner,
|
|
deps.settings.mergePatchOwner("config", request.params.owner, request.body ?? {}),
|
|
)
|
|
} catch (error) {
|
|
reply.code(400)
|
|
return { error: error instanceof Error ? error.message : "Invalid patch" }
|
|
}
|
|
})
|
|
|
|
app.get("/api/storage/state", async () => deps.settings.getDoc("state"))
|
|
app.patch("/api/storage/state", async (request, reply) => {
|
|
try {
|
|
return deps.settings.mergePatchDoc("state", request.body ?? {})
|
|
} catch (error) {
|
|
reply.code(400)
|
|
return { error: error instanceof Error ? error.message : "Invalid patch" }
|
|
}
|
|
})
|
|
|
|
app.get<{ Params: { owner: string } }>("/api/storage/state/:owner", async (request) => {
|
|
return deps.settings.getOwner("state", request.params.owner)
|
|
})
|
|
|
|
app.patch<{ Params: { owner: string } }>("/api/storage/state/:owner", async (request, reply) => {
|
|
try {
|
|
return deps.settings.mergePatchOwner("state", request.params.owner, request.body ?? {})
|
|
} catch (error) {
|
|
reply.code(400)
|
|
return { error: error instanceof Error ? error.message : "Invalid patch" }
|
|
}
|
|
})
|
|
|
|
// Binary validation helper (used by UI when adding binaries)
|
|
app.post("/api/storage/binaries/validate", async (request, reply) => {
|
|
try {
|
|
const body = ValidateBinarySchema.parse(request.body ?? {})
|
|
return validateBinaryPath(body.path)
|
|
} catch (error) {
|
|
deps.logger.warn({ err: error }, "Failed to validate binary")
|
|
reply.code(400)
|
|
return { valid: false, error: error instanceof Error ? error.message : "Invalid request" }
|
|
}
|
|
})
|
|
}
|