kopia lustrzana https://github.com/cloudflare/wildebeest
add neon client
rodzic
1b830d8df2
commit
c3fb612d21
|
@ -273,6 +273,7 @@ jobs:
|
||||||
echo -e "DOMAIN=\"${{ vars.CF_DEPLOY_DOMAIN }}\"\n" >> consumer/wrangler.toml
|
echo -e "DOMAIN=\"${{ vars.CF_DEPLOY_DOMAIN }}\"\n" >> consumer/wrangler.toml
|
||||||
echo -e "ADMIN_EMAIL=\"${{ vars.ADMIN_EMAIL }}\"\n" >> consumer/wrangler.toml
|
echo -e "ADMIN_EMAIL=\"${{ vars.ADMIN_EMAIL }}\"\n" >> consumer/wrangler.toml
|
||||||
|
|
||||||
|
yarn
|
||||||
yarn --cwd consumer/
|
yarn --cwd consumer/
|
||||||
echo "******"
|
echo "******"
|
||||||
command: publish --config consumer/wrangler.toml
|
command: publish --config consumer/wrangler.toml
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Env } from 'wildebeest/backend/src/types/env'
|
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||||
import d1 from './d1'
|
import d1 from './d1'
|
||||||
|
import neon from './neon'
|
||||||
|
|
||||||
export interface Result<T = unknown> {
|
export interface Result<T = unknown> {
|
||||||
results?: T[]
|
results?: T[]
|
||||||
|
@ -32,6 +33,10 @@ export interface QueryBuilder {
|
||||||
insertOrIgnore(q: string): string
|
insertOrIgnore(q: string): string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDatabase(env: Pick<Env, 'DATABASE'>): Promise<Database> {
|
export async function getDatabase(env: Pick<Env, 'DATABASE' | 'NEON_DATABASE_URL'>): Promise<Database> {
|
||||||
|
if (env.NEON_DATABASE_URL !== undefined) {
|
||||||
|
return neon(env)
|
||||||
|
}
|
||||||
|
|
||||||
return d1(env)
|
return d1(env)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
import * as neon from '@neondatabase/serverless'
|
||||||
|
import type { Database, Result, QueryBuilder } from 'wildebeest/backend/src/database'
|
||||||
|
import type { Env } from 'wildebeest/backend/src/types/env'
|
||||||
|
|
||||||
|
function sqliteToPsql(query: string): string {
|
||||||
|
let c = 0
|
||||||
|
return query.replaceAll(/\?([0-9])?/g, (match: string, p1: string) => {
|
||||||
|
c += 1
|
||||||
|
return `$${p1 || c}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const qb: QueryBuilder = {
|
||||||
|
jsonExtract(obj: string, prop: string): string {
|
||||||
|
return `json_extract_path(${obj}::json, '${prop}')::text`
|
||||||
|
},
|
||||||
|
|
||||||
|
jsonExtractIsNull(obj: string, prop: string): string {
|
||||||
|
return `${qb.jsonExtract(obj, prop)} = 'null'`
|
||||||
|
},
|
||||||
|
|
||||||
|
set(array: string): string {
|
||||||
|
return `(SELECT value::text FROM json_array_elements_text(${array}))`
|
||||||
|
},
|
||||||
|
|
||||||
|
epoch(): string {
|
||||||
|
return 'epoch'
|
||||||
|
},
|
||||||
|
|
||||||
|
insertOrIgnore(q: string): string {
|
||||||
|
return `INSERT ${q} ON CONFLICT DO NOTHING`
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function make(env: Pick<Env, 'NEON_DATABASE_URL'>): Promise<Database> {
|
||||||
|
const client = new neon.Client(env.NEON_DATABASE_URL)
|
||||||
|
await client.connect()
|
||||||
|
|
||||||
|
return {
|
||||||
|
qb,
|
||||||
|
|
||||||
|
prepare(query: string) {
|
||||||
|
return new PreparedStatement(env, query, [], client)
|
||||||
|
},
|
||||||
|
|
||||||
|
dump() {
|
||||||
|
throw new Error('not implemented')
|
||||||
|
},
|
||||||
|
|
||||||
|
async batch<T = unknown>(statements: PreparedStatement[]): Promise<Result<T>[]> {
|
||||||
|
throw new Error('not implemented')
|
||||||
|
console.log(statements)
|
||||||
|
},
|
||||||
|
|
||||||
|
async exec<T = unknown>(query: string): Promise<Result<T>> {
|
||||||
|
throw new Error('not implemented')
|
||||||
|
console.log(query)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PreparedStatement {
|
||||||
|
private env: Pick<Env, 'NEON_DATABASE_URL'>
|
||||||
|
private client: neon.Client
|
||||||
|
private query: string
|
||||||
|
private values: any[]
|
||||||
|
|
||||||
|
constructor(env: Pick<Env, 'NEON_DATABASE_URL'>, query: string, values: any[], client: neon.Client) {
|
||||||
|
this.env = env
|
||||||
|
this.query = query
|
||||||
|
this.values = values
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(...values: any[]): PreparedStatement {
|
||||||
|
return new PreparedStatement(this.env, this.query, [...this.values, ...values], this.client)
|
||||||
|
}
|
||||||
|
|
||||||
|
async first<T = unknown>(colName?: string): Promise<T> {
|
||||||
|
if (colName) {
|
||||||
|
throw new Error('not implemented')
|
||||||
|
}
|
||||||
|
const query = sqliteToPsql(this.query)
|
||||||
|
|
||||||
|
const results = await this.client.query(query, this.values)
|
||||||
|
if (results.rows.length !== 1) {
|
||||||
|
throw new Error(`expected a single row, returned ${results.rows.length} row(s)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.rows[0] as T
|
||||||
|
}
|
||||||
|
|
||||||
|
async run<T = unknown>(): Promise<Result<T>> {
|
||||||
|
return this.all()
|
||||||
|
}
|
||||||
|
|
||||||
|
async all<T = unknown>(): Promise<Result<T>> {
|
||||||
|
const query = sqliteToPsql(this.query)
|
||||||
|
const results = await this.client.query(query, this.values)
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: results.rows as T[],
|
||||||
|
success: true,
|
||||||
|
meta: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async raw<T = unknown>(): Promise<T[]> {
|
||||||
|
throw new Error('not implemented')
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,4 +25,6 @@ export interface Env {
|
||||||
SENTRY_DSN: string
|
SENTRY_DSN: string
|
||||||
SENTRY_ACCESS_CLIENT_ID: string
|
SENTRY_ACCESS_CLIENT_ID: string
|
||||||
SENTRY_ACCESS_CLIENT_SECRET: string
|
SENTRY_ACCESS_CLIENT_SECRET: string
|
||||||
|
|
||||||
|
NEON_DATABASE_URL?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
"deploy": "yarn build && yarn database:migrate && yarn pages publish frontend/dist --project-name=wildebeest"
|
"deploy": "yarn build && yarn database:migrate && yarn pages publish frontend/dist --project-name=wildebeest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@neondatabase/serverless": "^0.2.5",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"http-message-signatures": "^0.1.2",
|
"http-message-signatures": "^0.1.2",
|
||||||
|
|
|
@ -807,6 +807,11 @@
|
||||||
undici "5.9.1"
|
undici "5.9.1"
|
||||||
ws "^8.2.2"
|
ws "^8.2.2"
|
||||||
|
|
||||||
|
"@neondatabase/serverless@^0.2.5":
|
||||||
|
version "0.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@neondatabase/serverless/-/serverless-0.2.5.tgz#78bd4a905f50d087d06f16c02711fdd05b2a851d"
|
||||||
|
integrity sha512-Qu/nNZftfoqw4ojVCXU/EgYlfII3mzLm82iXNOUljFumPhoZ/Wp8NJG5DgSAKCWC0zwTyJsojdPLQDj/UPs2vg==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
|
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
|
||||||
|
|
Ładowanie…
Reference in New Issue