From e8088594635605618e09c54d1b3c36fd4bd24d38 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Mon, 16 Sep 2024 21:47:24 -0400 Subject: [PATCH] all-in one pgkit ui (updated 22:08) --- packages/admin/src/server/index.ts | 5 ++- packages/admin/src/server/middleware.ts | 53 ++++++++++++++----------- packages/migrator/src/router.ts | 39 +++++++++--------- packages/pgkit/package.json | 12 +++++- packages/pgkit/src/cli.ts | 6 +++ packages/pgkit/src/config.ts | 2 +- packages/pgkit/src/router.ts | 53 +++++++++++++++++++++++++ packages/pgkit/tsconfig.lib.json | 24 +++++------ pnpm-lock.yaml | 18 +++++++++ 9 files changed, 153 insertions(+), 59 deletions(-) create mode 100644 packages/pgkit/src/cli.ts create mode 100644 packages/pgkit/src/router.ts diff --git a/packages/admin/src/server/index.ts b/packages/admin/src/server/index.ts index d87974ef..9f0b17c2 100644 --- a/packages/admin/src/server/index.ts +++ b/packages/admin/src/server/index.ts @@ -1,3 +1,4 @@ +import {Transactable} from '@pgkit/client' import express from 'express' import * as path from 'path' import {fileURLToPath} from 'url' @@ -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 } diff --git a/packages/admin/src/server/middleware.ts b/packages/admin/src/server/middleware.ts index 36eaba3c..45f88bfa 100644 --- a/packages/admin/src/server/middleware.ts +++ b/packages/admin/src/server/middleware.ts @@ -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' @@ -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), + } + }, + }) +} diff --git a/packages/migrator/src/router.ts b/packages/migrator/src/router.ts index d59e7b7f..c337fff1 100644 --- a/packages/migrator/src/router.ts +++ b/packages/migrator/src/router.ts @@ -271,27 +271,28 @@ export const createMigratorRouter = (procedure: TRPCProcedureLike { - 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)) }), }) diff --git a/packages/pgkit/package.json b/packages/pgkit/package.json index 9e802aab..f9c00481 100644 --- a/packages/pgkit/package.json +++ b/packages/pgkit/package.json @@ -14,6 +14,9 @@ "types": "./dist/client.d.ts" } }, + "bin": { + "pgkit": "./dist/cli.js" + }, "author": "mmkal", "repository": { "type": "git", @@ -21,18 +24,25 @@ "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" } } diff --git a/packages/pgkit/src/cli.ts b/packages/pgkit/src/cli.ts new file mode 100644 index 00000000..2a732225 --- /dev/null +++ b/packages/pgkit/src/cli.ts @@ -0,0 +1,6 @@ +import * as trpcCli from 'trpc-cli' +import {router} from './router' + +export const cli = trpcCli.createCli({router}) + +void cli.run() diff --git a/packages/pgkit/src/config.ts b/packages/pgkit/src/config.ts index e9a74519..e154cebe 100644 --- a/packages/pgkit/src/config.ts +++ b/packages/pgkit/src/config.ts @@ -11,7 +11,7 @@ export type Config = { migrator?: { connectionString?: string /** @default '${cwd}/migrations' */ - migrationsTableName?: string + migrationTableName?: string /** @default 'migrations' */ migrationsPath?: string } diff --git a/packages/pgkit/src/router.ts b/packages/pgkit/src/router.ts new file mode 100644 index 00000000..3f67cf2e --- /dev/null +++ b/packages/pgkit/src/router.ts @@ -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().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 => {}) + }), +}) diff --git a/packages/pgkit/tsconfig.lib.json b/packages/pgkit/tsconfig.lib.json index 54abc191..b7336e5a 100644 --- a/packages/pgkit/tsconfig.lib.json +++ b/packages/pgkit/tsconfig.lib.json @@ -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" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 181ed0bf..a39046d5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -578,13 +578,31 @@ importers: packages/pgkit: dependencies: + '@pgkit/admin': + specifier: workspace:^ + version: link:../admin '@pgkit/client': specifier: workspace:^ version: link:../client + '@pgkit/migrator': + specifier: workspace:^ + version: link:../migrator + '@pgkit/typegen': + specifier: workspace:^ + version: link:../typegen + express: + specifier: ^4.18.2 + version: 4.18.2 importx: specifier: ^0.4.4 version: 0.4.4 + trpc-cli: + specifier: https://pkg.pr.new/mmkal/trpc-cli@38 + version: https://pkg.pr.new/mmkal/trpc-cli@38(@trpc/server@10.45.2)(zod@3.23.8) devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 '@types/node': specifier: ^20.11.20 version: 20.11.21