Skip to content

Commit

Permalink
all-in one pgkit ui (updated 22:08)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmkal committed Sep 17, 2024
1 parent cdb0244 commit e808859
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 59 deletions.
5 changes: 3 additions & 2 deletions packages/admin/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Transactable} from '@pgkit/client'
import express from 'express'
import * as path from 'path'
import {fileURLToPath} from 'url'
Expand All @@ -13,10 +14,10 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url))
* If you need to add more functionality, such as auth, or CORS, create your own express application
* and add the router before (or after) your middleware.
*/
export const getExpressRouter = (): express.RequestHandler => {
export const getExpressRouter = (client?: Transactable | string): express.RequestHandler => {
const router = express.Router()
router.use(clientMiddleware)
router.use(apiMiddleware)
router.use(apiMiddleware(client))
return router
}

Expand Down
53 changes: 30 additions & 23 deletions packages/admin/src/server/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// import {createHTTPServer} from '@trpc/server'
import {createClient} from '@pgkit/client'
import {createClient, Transactable} from '@pgkit/client'
import {createExpressMiddleware} from '@trpc/server/adapters/express'
import pMemoize from 'p-memoize'
import {appRouter} from './router.js'
Expand All @@ -8,25 +8,32 @@ const createClientMemoized = pMemoize(async (connectionString: string) => {
return createClient(connectionString)
})

export const apiMiddleware = createExpressMiddleware({
router: appRouter,
onError: props => {
let error: Error = props.error
const loggable: unknown[] = []
while (error?.cause) {
loggable.push(`${error.stack?.split('\n')[1]} caused by 👇`)
error = error.cause as Error
}
loggable.push(error)
console.error('trpc error', loggable)
},
createContext: async ({req}) => {
const connectionString =
req.headers['connection-string']?.toString() ||
process.env.PG_CONNECTION_STRING ||
'postgres://postgres:postgres@localhost:5432/postgres'
return {
connection: await createClientMemoized(connectionString),
}
},
})
export const apiMiddleware = (client?: Transactable | string) => {
return createExpressMiddleware({
router: appRouter,
onError: props => {
let error: Error = props.error
const loggable: unknown[] = []
while (error?.cause) {
loggable.push(`${error.stack?.split('\n')[1]} caused by 👇`)
error = error.cause as Error
}
loggable.push(error)
console.error('trpc error', loggable)
},
createContext: async ({req}) => {
if (typeof client === 'object') {
return {connection: client}
}

const connectionString =
req.headers['connection-string']?.toString() ||
process.env.PGKIT_CONNECTION_STRING ||
'postgres://postgres:postgres@localhost:5432/postgres'

return {
connection: await createClientMemoized(connectionString),
}
},
})
}
39 changes: 20 additions & 19 deletions packages/migrator/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,27 +271,28 @@ export const createMigratorRouter = (procedure: TRPCProcedureLike<MigratorRouter
'Query the database. Not strictly related to migrations, but can be used for debugging. Use with caution!',
})
.input(
z.object({
query: z.string(),
singlequote: z
.string()
.describe("Character to use in place of ' - use to avoid having to do bash quote-escaping")
.optional(),
doublequote: z
.string()
.describe('Character to use in place of " - use to avoid having to do bash quote-escaping')
.optional(),
method: z
.enum(['any', 'many', 'one', 'maybeOne', 'query', 'anyFirst', 'oneFirst', 'maybeOneFirst'])
.default('any'),
}),
z.tuple([
z.string(),
z.object({
singlequote: z
.string()
.describe("Character to use in place of ' - use to avoid having to do bash quote-escaping")
.optional(),
doublequote: z
.string()
.describe('Character to use in place of " - use to avoid having to do bash quote-escaping')
.optional(),
method: z
.enum(['any', 'many', 'one', 'maybeOne', 'query', 'anyFirst', 'oneFirst', 'maybeOneFirst'])
.default('any'),
}),
]),
)
.mutation(async ({input, ctx}) => {
let query = input.query
if (input.singlequote) query = query.replaceAll(input.singlequote, `'`)
if (input.doublequote) query = query.replaceAll(input.doublequote, `"`)
.mutation(async ({input: [query, options], ctx}) => {
if (options.singlequote) query = query.replaceAll(options.singlequote, `'`)
if (options.doublequote) query = query.replaceAll(options.doublequote, `"`)

return ctx.migrator.client[input.method](sql.raw(query))
return ctx.migrator.client[options.method](sql.raw(query))
}),
})

Expand Down
12 changes: 11 additions & 1 deletion packages/pgkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,35 @@
"types": "./dist/client.d.ts"
}
},
"bin": {
"pgkit": "./dist/cli.js"
},
"author": "mmkal",
"repository": {
"type": "git",
"url": "https://github.com/mmkal/pgkit",
"directory": "packages/pgkit"
},
"scripts": {
"dogfood": "tsx src/cli.ts",
"prepack": "pnpm build",
"build": "tsc --sourceMap -p tsconfig.lib.json",
"test": "vitest run"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.11.20",
"eslint": "^8.57.0",
"eslint-plugin-mmkal": "0.10.1",
"vitest": "^1.2.2"
},
"dependencies": {
"@pgkit/admin": "workspace:^",
"@pgkit/client": "workspace:^",
"importx": "^0.4.4"
"@pgkit/migrator": "workspace:^",
"@pgkit/typegen": "workspace:^",
"express": "^4.18.2",
"importx": "^0.4.4",
"trpc-cli": "https://pkg.pr.new/mmkal/trpc-cli@38"
}
}
6 changes: 6 additions & 0 deletions packages/pgkit/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as trpcCli from 'trpc-cli'
import {router} from './router'

export const cli = trpcCli.createCli({router})

void cli.run()
2 changes: 1 addition & 1 deletion packages/pgkit/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type Config = {
migrator?: {
connectionString?: string
/** @default '${cwd}/migrations' */
migrationsTableName?: string
migrationTableName?: string
/** @default 'migrations' */
migrationsPath?: string
}
Expand Down
53 changes: 53 additions & 0 deletions packages/pgkit/src/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {Client, createClient} from '@pgkit/client'
import {Migrator, createMigratorRouter} from '@pgkit/migrator'
import {confirm} from '@pgkit/migrator/dist/cli'
import {router as typegenRouter} from '@pgkit/typegen'
import express from 'express'
import * as trpcCli from 'trpc-cli'
import {z} from 'trpc-cli'
import {loadConfig} from './config'

const t = trpcCli.trpcServer.initTRPC.meta<trpcCli.TrpcCliMeta>().create()

const procedureWithClient = t.procedure.use(async ({ctx, next}) => {
const config = await loadConfig()
const client = (clientSingleton.client ||= createClient(config.client.connectionString))
const migrator = (clientSingleton.migrator ||= new Migrator({
client,
migrationsPath: config.migrator?.migrationsPath,
migrationTableName: config.migrator?.migrationTableName,
}))
return next({
ctx: {...ctx, client, migrator},
})
})

const clientSingleton = {
client: null as null | Client,
migrator: null as null | Migrator,
}

export const router = t.router({
migrate: createMigratorRouter(
procedureWithClient.use(async ({ctx, next}) => {
return next({ctx: {...ctx, confirm}})
}),
),
...typegenRouter._def.procedures,
admin: procedureWithClient
.input(
z.object({
port: z.number().default(7002),
}),
)
.mutation(async ({input, ctx}) => {
const {getExpressRouter} = await import('@pgkit/admin')
const app = express()
app.use(getExpressRouter(ctx.client))
app.listen(input.port, () => {
// eslint-disable-next-line no-console
console.log(`Admin UI listening on http://localhost:${input.port}`)
})
return new Promise(_r => {})
}),
})
24 changes: 11 additions & 13 deletions packages/pgkit/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
{
"extends": [
"./tsconfig.json"
],
"include": [
"src"
],
"compilerOptions": {
"outDir": "dist",
"declaration": true,
"sourceMap": true,
"noEmit": false,
"module": "Node16"
}
"extends": ["./tsconfig.json"],
"include": ["src"],
"compilerOptions": {
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmit": false,
"skipLibCheck": true,
"module": "Node16"
}
}
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e808859

Please sign in to comment.