kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/715/head
rodzic
7dd05666ce
commit
30d909c6f1
|
@ -5,6 +5,7 @@ import * as middleware from '@/lib/middleware'
|
||||||
|
|
||||||
import { registerHealthCheck } from './health-check'
|
import { registerHealthCheck } from './health-check'
|
||||||
import { registerV1UsersGetUser } from './users/get-user'
|
import { registerV1UsersGetUser } from './users/get-user'
|
||||||
|
import { registerV1UsersUpdateUser } from './users/update-user'
|
||||||
|
|
||||||
export const apiV1 = new OpenAPIHono()
|
export const apiV1 = new OpenAPIHono()
|
||||||
|
|
||||||
|
@ -13,8 +14,9 @@ const pri = new OpenAPIHono<AuthenticatedEnv>()
|
||||||
|
|
||||||
registerHealthCheck(pub)
|
registerHealthCheck(pub)
|
||||||
|
|
||||||
// users
|
// users crud
|
||||||
registerV1UsersGetUser(pri)
|
registerV1UsersGetUser(pri)
|
||||||
|
registerV1UsersUpdateUser(pri)
|
||||||
|
|
||||||
apiV1.route('/', pub)
|
apiV1.route('/', pub)
|
||||||
apiV1.use(middleware.authenticate)
|
apiV1.use(middleware.authenticate)
|
||||||
|
|
|
@ -9,8 +9,7 @@ const ParamsSchema = z.object({
|
||||||
param: {
|
param: {
|
||||||
name: 'userId',
|
name: 'userId',
|
||||||
in: 'path'
|
in: 'path'
|
||||||
},
|
}
|
||||||
example: 'pfh0haxfpzowht3oi213cqos'
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -37,13 +36,8 @@ const route = createRoute({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export type Route = typeof route
|
export function registerV1UsersGetUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
||||||
export type V1UsersGetUserResponse = z.infer<
|
return app.openapi(route, async (c) => {
|
||||||
(typeof route.responses)[200]['content']['application/json']['schema']
|
|
||||||
>
|
|
||||||
|
|
||||||
export const registerV1UsersGetUser = (app: OpenAPIHono<AuthenticatedEnv>) =>
|
|
||||||
app.openapi(route, async (c) => {
|
|
||||||
const { userId } = c.req.valid('param')
|
const { userId } = c.req.valid('param')
|
||||||
|
|
||||||
const user = await db.query.users.findFirst({
|
const user = await db.query.users.findFirst({
|
||||||
|
@ -53,3 +47,4 @@ export const registerV1UsersGetUser = (app: OpenAPIHono<AuthenticatedEnv>) =>
|
||||||
|
|
||||||
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
|
import { db, eq, schema, userIdSchema } from '@/db'
|
||||||
|
import { assert, parseZodSchema } from '@/lib/utils'
|
||||||
|
|
||||||
|
const ParamsSchema = z.object({
|
||||||
|
userId: userIdSchema.openapi({
|
||||||
|
param: {
|
||||||
|
name: 'userId',
|
||||||
|
in: 'path'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
tags: ['users'],
|
||||||
|
operationId: 'updateUser',
|
||||||
|
method: 'put',
|
||||||
|
path: 'users/{userId}',
|
||||||
|
security: [{ bearerAuth: [] }],
|
||||||
|
request: {
|
||||||
|
params: ParamsSchema,
|
||||||
|
body: {
|
||||||
|
required: true,
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: schema.userUpdateSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'A user object',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: schema.userSelectSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// ...openApiErrorResponses
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function registerV1UsersUpdateUser(app: OpenAPIHono<AuthenticatedEnv>) {
|
||||||
|
return app.openapi(route, async (c) => {
|
||||||
|
const { userId } = c.req.valid('param')
|
||||||
|
const body = c.req.valid('json')
|
||||||
|
|
||||||
|
const user = await db
|
||||||
|
.update(schema.users)
|
||||||
|
.set(body)
|
||||||
|
.where(eq(schema.users.id, userId))
|
||||||
|
.returning()
|
||||||
|
assert(user, 404, `User not found: ${userId}`)
|
||||||
|
|
||||||
|
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
|
||||||
|
// TODO: Currently unused after forking @fisch0920/drizzle-zod.
|
||||||
|
export function makeNullablePropsOptional<Schema extends z.AnyZodObject>(
|
||||||
|
schema: Schema
|
||||||
|
): z.ZodObject<{
|
||||||
|
[key in keyof Schema['shape']]: Schema['shape'][key] extends z.ZodNullable<
|
||||||
|
infer T
|
||||||
|
>
|
||||||
|
? z.ZodOptional<T>
|
||||||
|
: Schema['shape'][key]
|
||||||
|
}> {
|
||||||
|
const entries = Object.entries(schema.shape)
|
||||||
|
const newProps: any = {}
|
||||||
|
|
||||||
|
for (const [key, value] of entries) {
|
||||||
|
newProps[key] =
|
||||||
|
value instanceof z.ZodNullable ? value.unwrap().optional() : value
|
||||||
|
return newProps
|
||||||
|
}
|
||||||
|
|
||||||
|
return z.object(newProps) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ColumnType =
|
||||||
|
// string
|
||||||
|
| 'text'
|
||||||
|
| 'varchar'
|
||||||
|
| 'timestamp'
|
||||||
|
| 'stripeId'
|
||||||
|
| 'projectId'
|
||||||
|
| 'deploymentId'
|
||||||
|
| 'cuid'
|
||||||
|
// boolean
|
||||||
|
| 'boolean'
|
||||||
|
// number
|
||||||
|
| 'integer'
|
||||||
|
| 'smallint'
|
||||||
|
| 'bigint'
|
||||||
|
// json
|
||||||
|
| 'json'
|
||||||
|
| 'jsonb'
|
||||||
|
|
||||||
|
export type ColumnTypeToTSType<T extends ColumnType> = T extends
|
||||||
|
| 'text'
|
||||||
|
| 'varchar'
|
||||||
|
| 'timestamp'
|
||||||
|
| 'cuid'
|
||||||
|
| 'stripeId'
|
||||||
|
| 'projectId'
|
||||||
|
| 'deploymentId'
|
||||||
|
? string
|
||||||
|
: T extends 'boolean'
|
||||||
|
? boolean
|
||||||
|
: T extends 'integer' | 'smallint' | 'bigint'
|
||||||
|
? number
|
||||||
|
: never
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/drizzle-team/@fisch0920/drizzle-orm/issues/2745
|
||||||
|
*/
|
||||||
|
function optional<
|
||||||
|
T extends ColumnType,
|
||||||
|
InferredType extends
|
||||||
|
| string
|
||||||
|
| boolean
|
||||||
|
| number
|
||||||
|
| object = ColumnTypeToTSType<T>
|
||||||
|
>(dataType: T) {
|
||||||
|
return customType<{
|
||||||
|
data: InferredType | undefined
|
||||||
|
driverData: InferredType | null
|
||||||
|
config: T extends 'stripeId'
|
||||||
|
? {
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
: never
|
||||||
|
}>({
|
||||||
|
dataType() {
|
||||||
|
if (dataType === 'stripeId') {
|
||||||
|
return 'varchar({ length: 255 })'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType === 'cuid') {
|
||||||
|
return 'varchar({ length: 24 })'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType === 'projectId') {
|
||||||
|
return 'varchar({ length: 130 })'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType === 'deploymentId') {
|
||||||
|
return 'varchar({ length: 160 })'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType === 'timestamp') {
|
||||||
|
return 'timestamp({ mode: "string" })'
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataType
|
||||||
|
},
|
||||||
|
fromDriver: (v) => v ?? undefined,
|
||||||
|
toDriver: (v) => v ?? null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const optionalText = optional('text')
|
||||||
|
export const optionalTimestamp = optional('timestamp')
|
||||||
|
export const optionalBoolean = optional('boolean')
|
||||||
|
export const optionalVarchar = optional('varchar')
|
||||||
|
export const optionalCuid = optional('cuid')
|
||||||
|
export const optionalStripeId = optional('stripeId')
|
||||||
|
export const optionalProjectId = optional('projectId')
|
||||||
|
export const optionalDeploymentId = optional('deploymentId')
|
|
@ -89,4 +89,9 @@ export const userSelectSchema = createSelectSchema(users, {
|
||||||
providers: authProvidersSchema
|
providers: authProvidersSchema
|
||||||
}).openapi('User')
|
}).openapi('User')
|
||||||
|
|
||||||
export const userUpdateSchema = createUpdateSchema(users)
|
export const userUpdateSchema = createUpdateSchema(users).pick({
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
image: true,
|
||||||
|
isStripeConnectEnabledByDefault: true
|
||||||
|
})
|
||||||
|
|
|
@ -13,6 +13,9 @@ import { createSchemaFactory } from '@fisch0920/drizzle-zod'
|
||||||
import { z } from '@hono/zod-openapi'
|
import { z } from '@hono/zod-openapi'
|
||||||
import { createId } from '@paralleldrive/cuid2'
|
import { createId } from '@paralleldrive/cuid2'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `cuid2`
|
||||||
|
*/
|
||||||
export function cuid<U extends string, T extends Readonly<[U, ...U[]]>>(
|
export function cuid<U extends string, T extends Readonly<[U, ...U[]]>>(
|
||||||
config?: PgVarcharConfig<T | Writable<T>, never>
|
config?: PgVarcharConfig<T | Writable<T>, never>
|
||||||
): PgVarcharBuilderInitial<'', Writable<T>, 24> {
|
): PgVarcharBuilderInitial<'', Writable<T>, 24> {
|
||||||
|
@ -43,6 +46,9 @@ export function deploymentId<U extends string, T extends Readonly<[U, ...U[]]>>(
|
||||||
return varchar({ length: 160, ...config })
|
return varchar({ length: 160, ...config })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default `id` primary key as a cuid2
|
||||||
|
*/
|
||||||
export const id = varchar('id', { length: 24 })
|
export const id = varchar('id', { length: 24 })
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(createId)
|
.$defaultFn(createId)
|
||||||
|
@ -81,116 +87,3 @@ export const { createInsertSchema, createSelectSchema, createUpdateSchema } =
|
||||||
date: true
|
date: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Currently unused after forking @fisch0920/drizzle-zod.
|
|
||||||
// export function makeNullablePropsOptional<Schema extends z.AnyZodObject>(
|
|
||||||
// schema: Schema
|
|
||||||
// ): z.ZodObject<{
|
|
||||||
// [key in keyof Schema['shape']]: Schema['shape'][key] extends z.ZodNullable<
|
|
||||||
// infer T
|
|
||||||
// >
|
|
||||||
// ? z.ZodOptional<T>
|
|
||||||
// : Schema['shape'][key]
|
|
||||||
// }> {
|
|
||||||
// const entries = Object.entries(schema.shape)
|
|
||||||
// const newProps: any = {}
|
|
||||||
|
|
||||||
// for (const [key, value] of entries) {
|
|
||||||
// newProps[key] =
|
|
||||||
// value instanceof z.ZodNullable ? value.unwrap().optional() : value
|
|
||||||
// return newProps
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return z.object(newProps) as any
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export type ColumnType =
|
|
||||||
// // string
|
|
||||||
// | 'text'
|
|
||||||
// | 'varchar'
|
|
||||||
// | 'timestamp'
|
|
||||||
// | 'stripeId'
|
|
||||||
// | 'projectId'
|
|
||||||
// | 'deploymentId'
|
|
||||||
// | 'cuid'
|
|
||||||
// // boolean
|
|
||||||
// | 'boolean'
|
|
||||||
// // number
|
|
||||||
// | 'integer'
|
|
||||||
// | 'smallint'
|
|
||||||
// | 'bigint'
|
|
||||||
// // json
|
|
||||||
// | 'json'
|
|
||||||
// | 'jsonb'
|
|
||||||
|
|
||||||
// export type ColumnTypeToTSType<T extends ColumnType> = T extends
|
|
||||||
// | 'text'
|
|
||||||
// | 'varchar'
|
|
||||||
// | 'timestamp'
|
|
||||||
// | 'cuid'
|
|
||||||
// | 'stripeId'
|
|
||||||
// | 'projectId'
|
|
||||||
// | 'deploymentId'
|
|
||||||
// ? string
|
|
||||||
// : T extends 'boolean'
|
|
||||||
// ? boolean
|
|
||||||
// : T extends 'integer' | 'smallint' | 'bigint'
|
|
||||||
// ? number
|
|
||||||
// : never
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @see https://github.com/drizzle-team/@fisch0920/drizzle-orm/issues/2745
|
|
||||||
// */
|
|
||||||
// function optional<
|
|
||||||
// T extends ColumnType,
|
|
||||||
// InferredType extends
|
|
||||||
// | string
|
|
||||||
// | boolean
|
|
||||||
// | number
|
|
||||||
// | object = ColumnTypeToTSType<T>
|
|
||||||
// >(dataType: T) {
|
|
||||||
// return customType<{
|
|
||||||
// data: InferredType | undefined
|
|
||||||
// driverData: InferredType | null
|
|
||||||
// config: T extends 'stripeId'
|
|
||||||
// ? {
|
|
||||||
// length: number
|
|
||||||
// }
|
|
||||||
// : never
|
|
||||||
// }>({
|
|
||||||
// dataType() {
|
|
||||||
// if (dataType === 'stripeId') {
|
|
||||||
// return 'varchar({ length: 255 })'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (dataType === 'cuid') {
|
|
||||||
// return 'varchar({ length: 24 })'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (dataType === 'projectId') {
|
|
||||||
// return 'varchar({ length: 130 })'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (dataType === 'deploymentId') {
|
|
||||||
// return 'varchar({ length: 160 })'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (dataType === 'timestamp') {
|
|
||||||
// return 'timestamp({ mode: "string" })'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return dataType
|
|
||||||
// },
|
|
||||||
// fromDriver: (v) => v ?? undefined,
|
|
||||||
// toDriver: (v) => v ?? null
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export const optionalText = optional('text')
|
|
||||||
// export const optionalTimestamp = optional('timestamp')
|
|
||||||
// export const optionalBoolean = optional('boolean')
|
|
||||||
// export const optionalVarchar = optional('varchar')
|
|
||||||
// export const optionalCuid = optional('cuid')
|
|
||||||
// export const optionalStripeId = optional('stripeId')
|
|
||||||
// export const optionalProjectId = optional('projectId')
|
|
||||||
// export const optionalDeploymentId = optional('deploymentId')
|
|
||||||
|
|
Ładowanie…
Reference in New Issue