sven/neon
Sven Sauleau 2023-02-22 16:49:51 +00:00
rodzic 2beb65f9e2
commit 575aecd33f
72 zmienionych plików z 1354 dodań i 755 usunięć

Wyświetl plik

@ -2,7 +2,7 @@ name: Deploy
on:
push:
branches:
- main
- sven/neon
repository_dispatch:
jobs:
deploy:
@ -273,6 +273,7 @@ jobs:
echo -e "DOMAIN=\"${{ vars.CF_DEPLOY_DOMAIN }}\"\n" >> consumer/wrangler.toml
echo -e "ADMIN_EMAIL=\"${{ vars.ADMIN_EMAIL }}\"\n" >> consumer/wrangler.toml
yarn
yarn --cwd consumer/
echo "******"
command: publish --config consumer/wrangler.toml

Wyświetl plik

@ -24,7 +24,7 @@ export async function getAccount(domain: string, accountId: string, db: Database
}
}
async function getRemoteAccount(handle: Handle, acct: string, db: D1Database): Promise<MastodonAccount | null> {
async function getRemoteAccount(handle: Handle, acct: string, db: Database): Promise<MastodonAccount | null> {
// TODO: using webfinger isn't the optimal implementation. We could cache
// the object in D1 and directly query the remote API, indicated by the actor's
// url field. For now, let's keep it simple.

Wyświetl plik

@ -9,13 +9,18 @@ export async function getPeers(db: Database): Promise<Array<String>> {
}
export async function addPeer(db: Database, domain: string): Promise<void> {
const query = `
INSERT OR IGNORE INTO peers (domain)
VALUES (?)
`
try {
const query = `
INSERT INTO peers (domain)
VALUES (?)
`
const out = await db.prepare(query).bind(domain).run()
if (!out.success) {
throw new Error('SQL error: ' + out.error)
const out = await db.prepare(query).bind(domain).run()
if (!out.success) {
throw new Error('SQL error: ' + out.error)
}
} catch (err: any) {
console.warn(err.stack);
// handle peer already exists for psql
}
}

Wyświetl plik

@ -1,6 +1,31 @@
import { type Database } from 'wildebeest/backend/src/database'
import { type Database, QueryBuilder } from 'wildebeest/backend/src/database'
import type { Env } from 'wildebeest/backend/src/types/env'
export default function make(env: Env): Database {
return env.DATABASE
const qb: QueryBuilder = {
jsonExtract(obj: string, prop: string): string {
return `json_extract(${obj}, '$.${prop}')`
},
jsonExtractIsNull(obj: string, prop: string): string {
return `${qb.jsonExtract(obj, prop)} IS NULL`
},
set(array: string): string {
return `(SELECT value FROM json_each(${array}))`
},
psql(raw: string): string {
return ''
},
epoch(): string {
return '00-00-00 00:00:00'
},
}
export default function make(env: Env): Database {
const db = env.DATABASE as any
db.qb = qb
return db as Database
}

Wyświetl plik

@ -1,5 +1,6 @@
import type { Env } from 'wildebeest/backend/src/types/env'
import d1 from './d1'
import neon from './neon'
export interface Result<T = unknown> {
results?: T[]
@ -9,6 +10,7 @@ export interface Result<T = unknown> {
}
export interface Database {
qb: QueryBuilder
prepare(query: string): PreparedStatement
dump(): Promise<ArrayBuffer>
batch<T = unknown>(statements: PreparedStatement[]): Promise<Result<T>[]>
@ -23,6 +25,20 @@ export interface PreparedStatement {
raw<T = unknown>(): Promise<T[]>
}
export function getDatabase(env: Env): Database {
return d1(env)
export interface QueryBuilder {
jsonExtract(obj: string, prop: string): string
jsonExtractIsNull(obj: string, prop: string): string
set(array: string): string
psql(raw: string): string
epoch(): string
}
const isTesting = typeof jest !== 'undefined'
export async function getDatabase(env: Env): Promise<Database> {
if (isTesting) {
return d1(env)
} else {
return neon(env)
}
}

Wyświetl plik

@ -0,0 +1,105 @@
import * as pg from 'pg'
import type { Database, Result } 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}`
})
}
export default function make(env: Env): Database {
return {
prepare(query: string) {
return new PreparedStatement(env, query, [])
},
dump() {
throw new Error('not implemented')
},
async batch<T = unknown>(statements: PreparedStatement[]): Promise<Result<T>[]> {
console.log(statements)
throw new Error('not implemented')
},
async exec<T = unknown>(query: string): Promise<Result<T>> {
console.log(query)
throw new Error('not implemented')
},
}
}
export class PreparedStatement {
private env: Env
private query: string
private values: any[]
constructor(env: Env, query: string, values: any[]) {
this.env = env
this.query = query
this.values = values
}
bind(...values: any[]): PreparedStatement {
return new PreparedStatement(this.env, this.query, [...this.values, ...values])
}
private async connect() {
const s = 'postgres://postgres:postgres@127.0.0.1/postgres'
const client = new pg.Client(s)
await client.connect()
return client
}
async first<T = unknown>(colName?: string): Promise<T> {
const conn = await this.connect()
try {
if (colName) {
throw new Error('not implemented')
}
const query = sqliteToPsql(this.query)
console.log(query, this.values)
const results = await conn.query(query, this.values)
console.log({ rowCount: results.rowCount })
if (results.rows.length !== 1) {
throw new Error(`expected a single row, returned ${results.rows.length} row(s)`)
}
return results.rows[0] as T
} finally {
await conn.end()
}
}
async run<T = unknown>(): Promise<Result<T>> {
return this.all()
}
async all<T = unknown>(): Promise<Result<T>> {
const conn = await this.connect()
try {
const query = sqliteToPsql(this.query)
console.log(query, this.values)
const results = await conn.query(query, this.values)
console.log({ rowCount: results.rowCount })
return {
results: results.rows as T[],
success: true,
meta: {},
}
} finally {
await conn.end()
}
}
async raw<T = unknown>(): Promise<T[]> {
throw new Error('not implemented')
}
}

Wyświetl plik

@ -0,0 +1,116 @@
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}))`
},
psql(raw: string): string {
return raw
},
epoch(): string {
return 'epoch'
},
}
export default async function make(env: Env): Promise<Database> {
const client = new neon.Client(env.NEON_DATABASE_URL!)
console.log(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>[]> {
console.log(statements)
throw new Error('not implemented')
},
async exec<T = unknown>(query: string): Promise<Result<T>> {
console.log(query)
throw new Error('not implemented')
},
}
}
export class PreparedStatement {
private env: Env
private client: neon.Client
private query: string
private values: any[]
constructor(env: Env, 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)
console.log(query, this.values)
const results = await this.client.query(query, this.values)
console.log({ results })
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)
console.log(query, this.values)
const results = await this.client.query(query, this.values)
console.log({ results })
return {
results: results.rows as T[],
success: true,
meta: {},
}
}
async raw<T = unknown>(): Promise<T[]> {
throw new Error('not implemented')
}
}

Wyświetl plik

@ -16,7 +16,7 @@ export async function getHomeTimeline(domain: string, db: Database, actor: Actor
`
SELECT
actor_following.target_actor_id as id,
json_extract(actors.properties, '$.followers') as actorFollowersURL
${db.qb.jsonExtract('actors.properties', 'followers')} as actorFollowersURL
FROM actor_following
INNER JOIN actors ON actors.id = actor_following.target_actor_id
WHERE actor_id=? AND state='accepted'
@ -60,10 +60,10 @@ INNER JOIN objects ON objects.id = outbox_objects.object_id
INNER JOIN actors ON actors.id = outbox_objects.actor_id
WHERE
objects.type = 'Note'
AND outbox_objects.actor_id IN (SELECT value FROM json_each(?2))
AND json_extract(objects.properties, '$.inReplyTo') IS NULL
AND (outbox_objects.target = '${PUBLIC_GROUP}' OR outbox_objects.target IN (SELECT value FROM json_each(?3)))
GROUP BY objects.id
AND outbox_objects.actor_id IN ${db.qb.set('?2')}
AND ${db.qb.jsonExtractIsNull('objects.properties', 'inReplyTo')}
AND (outbox_objects.target = '${PUBLIC_GROUP}' OR outbox_objects.target IN ${db.qb.set('?3')})
GROUP BY objects.id ${db.qb.psql(', actors.id, outbox_objects.actor_id, outbox_objects.published_date')}
ORDER by outbox_objects.published_date DESC
LIMIT ?4
`
@ -101,7 +101,7 @@ export enum LocalPreference {
function localPreferenceQuery(preference: LocalPreference): string {
switch (preference) {
case LocalPreference.NotSet:
return '1'
return 'true'
case LocalPreference.OnlyLocal:
return 'objects.local = 1'
case LocalPreference.OnlyRemote:
@ -136,10 +136,12 @@ INNER JOIN actors ON actors.id=outbox_objects.actor_id
LEFT JOIN note_hashtags ON objects.id=note_hashtags.object_id
WHERE objects.type='Note'
AND ${localPreferenceQuery(localPreference)}
AND json_extract(objects.properties, '$.inReplyTo') IS NULL
AND ${db.qb.jsonExtractIsNull('objects.properties', 'inReplyTo')}
AND outbox_objects.target = '${PUBLIC_GROUP}'
${hashtagFilter}
GROUP BY objects.id
GROUP BY objects.id ${db.qb.psql(
', actors.id, actors.cdate, actors.properties, outbox_objects.actor_id, outbox_objects.published_date'
)}
ORDER by outbox_objects.published_date DESC
LIMIT ?1 OFFSET ?2
`

Wyświetl plik

@ -97,7 +97,7 @@ export async function main(context: EventContext<Env, any, any>) {
// configuration, which are used to verify the JWT.
// TODO: since we don't load the instance configuration anymore, we
// don't need to load the user before anymore.
if (!(await loadContextData(getDatabase(context.env), clientId, payload.email, context))) {
if (!(await loadContextData(await getDatabase(context.env), clientId, payload.email, context))) {
return errors.notAuthorized('failed to load context data')
}

Wyświetl plik

@ -25,4 +25,12 @@ export interface Env {
SENTRY_DSN: string
SENTRY_ACCESS_CLIENT_ID: string
SENTRY_ACCESS_CLIENT_SECRET: string
NEON_DATABASE_URL?: string
PSQL_DATABASE_URL?: string
PSCALE_HOST?: string
PSCALE_USERNAME?: string
PSCALE_PASSWORD?: string
}

Wyświetl plik

@ -1,4 +1,5 @@
import * as actors from '../activitypub/actors'
import { type Database } from 'wildebeest/backend/src/database'
import type { Actor } from '../activitypub/actors'
export type WebFingerResponse = {
@ -11,7 +12,7 @@ const headers = {
accept: 'application/jrd+json',
}
export async function queryAcct(domain: string, db: D1Database, acct: string): Promise<Actor | null> {
export async function queryAcct(domain: string, db: Database, acct: string): Promise<Actor | null> {
const url = await queryAcctLink(domain, acct)
if (url === null) {
return null

Wyświetl plik

@ -76,12 +76,14 @@ describe('Mastodon APIs', () => {
})
test('GET /apps is bad request', async () => {
const db = await makeDB()
const vapidKeys = await generateVAPIDKeys()
const request = new Request('https://example.com')
const ctx: any = {
next: () => new Response(),
data: null,
env: {
DATABASE: db,
VAPID_JWK: JSON.stringify(vapidKeys),
},
request,

Wyświetl plik

@ -81,7 +81,7 @@ describe('Mastodon APIs', () => {
.prepare(
`
SELECT
json_extract(properties, '$.content') as content,
${db.qb.jsonExtract('properties', 'content')} as content,
original_actor_id,
original_object_id
FROM objects
@ -758,7 +758,7 @@ describe('Mastodon APIs', () => {
const row = await db
.prepare(
`
SELECT json_extract(properties, '$.inReplyTo') as inReplyTo
SELECT ${db.qb.jsonExtract('properties', 'inReplyTo')} as inReplyTo
FROM objects
WHERE mastodon_id=?
`

Wyświetl plik

@ -8,7 +8,8 @@ import { promises as fs } from 'fs'
import * as path from 'path'
import { BetaDatabase } from '@miniflare/d1'
import * as SQLiteDatabase from 'better-sqlite3'
import { type Database } from 'wildebeest/backend/src/database'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
import d1 from 'wildebeest/backend/src/database/d1'
export function isUrlValid(s: string) {
let url
@ -32,7 +33,8 @@ export async function makeDB(): Promise<Database> {
db.exec(content)
}
return db2 as unknown as Database
const env = { DATABASE: db2 } as any
return d1(env)
}
export function assertCORS(response: Response) {

Wyświetl plik

@ -3,9 +3,14 @@
"version": "0.0.0",
"devDependencies": {
"@cloudflare/workers-types": "^4.20221111.1",
"@neondatabase/serverless": "^0.2.5",
"toucan-js": "^3.1.0",
"typescript": "^4.9.4",
"wrangler": "2.7.1"
},
"private": true
"private": true,
"dependencies": {
"@planetscale/database": "^1.5.0",
"pg": "^8.9.0"
}
}

Wyświetl plik

@ -8,12 +8,12 @@ import { deliverToActor } from 'wildebeest/backend/src/activitypub/deliver'
export async function handleDeliverMessage(env: Env, actor: Actor, message: DeliverMessageBody) {
const toActorId = new URL(message.toActorId)
const targetActor = await actors.getAndCache(toActorId, getDatabase(env as any))
const targetActor = await actors.getAndCache(toActorId, await getDatabase(env as any))
if (targetActor === null) {
console.warn(`actor ${toActorId} not found`)
return
}
const signingKey = await getSigningKey(message.userKEK, getDatabase(env as any), actor)
const signingKey = await getSigningKey(message.userKEK, await getDatabase(env as any), actor)
await deliverToActor(signingKey, actor, targetActor, message.activity, env.DOMAIN)
}

Wyświetl plik

@ -9,7 +9,7 @@ import type { Env } from './'
export async function handleInboxMessage(env: Env, actor: Actor, message: InboxMessageBody) {
const domain = env.DOMAIN
const db = getDatabase(env as any)
const db = await getDatabase(env as any)
const adminEmail = env.ADMIN_EMAIL
const cache = cacheFromEnv(env)
const activity = message.activity

Wyświetl plik

@ -20,7 +20,7 @@ export type Env = {
export default {
async queue(batch: MessageBatch<MessageBody>, env: Env, ctx: ExecutionContext) {
const sentry = initSentryQueue(env, ctx)
const db = getDatabase(env as any)
const db = await getDatabase(env as any)
try {
for (const message of batch.messages) {

Plik diff jest za duży Load Diff

Wyświetl plik

@ -8,12 +8,13 @@ import { Avatar } from '~/components/avatar'
import { getPersonByEmail } from 'wildebeest/backend/src/activitypub/actors'
import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml'
import { buildRedirect } from 'wildebeest/functions/oauth/authorize'
import { getDatabase } from 'wildebeest/backend/src/database'
export const clientLoader = loader$<Promise<Client>, { DATABASE: D1Database }>(async ({ platform, query, html }) => {
const client_id = query.get('client_id') || ''
let client: Client | null = null
try {
client = await getClientById(platform.DATABASE, client_id)
client = await getClientById(await getDatabase(platform as any), client_id)
} catch (e: unknown) {
const error = e as { stack: string; cause: string }
console.warn(error.stack, error.cause)
@ -48,10 +49,10 @@ export const userLoader = loader$<
throw html(500, getErrorHtml("The Access JWT doesn't contain an email"))
}
const person = await getPersonByEmail(platform.DATABASE, payload.email)
const person = await getPersonByEmail(await getDatabase(platform as any), payload.email)
if (person === null) {
const isFirstLogin = true
const res = await buildRedirect(platform.DATABASE, request as Request, isFirstLogin, jwt.value)
const res = await buildRedirect(await getDatabase(platform as any), request as Request, isFirstLogin, jwt.value)
if (res.status === 302) {
throw redirect(302, res.headers.get('location') || '')
} else {

Wyświetl plik

@ -1,4 +1,5 @@
import { component$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { MastodonStatus, StatusContext } from '~/types'
import Status from '~/components/Status'
import * as statusAPI from 'wildebeest/functions/api/v1/statuses/[id]'
@ -17,7 +18,12 @@ export const statusLoader = loader$<
const domain = new URL(request.url).hostname
let statusText = ''
try {
const statusResponse = await statusAPI.handleRequestGet(platform.DATABASE, params.statusId, domain, {} as Person)
const statusResponse = await statusAPI.handleRequestGet(
await getDatabase(platform as any),
params.statusId,
domain,
{} as Person
)
statusText = await statusResponse.text()
} catch (e: unknown) {
const error = e as { stack: string; cause: string }
@ -31,7 +37,7 @@ export const statusLoader = loader$<
const statusTextContent = await getTextContent(status.content)
try {
const contextResponse = await contextAPI.handleRequest(domain, platform.DATABASE, params.statusId)
const contextResponse = await contextAPI.handleRequest(domain, await getDatabase(platform as any), params.statusId)
const contextText = await contextResponse.text()
const context = JSON.parse(contextText ?? null) as StatusContext | null
if (!context) {

Wyświetl plik

@ -1,4 +1,5 @@
import { $, component$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { loader$ } from '@builder.io/qwik-city'
import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml'
import type { MastodonStatus } from '~/types'
@ -21,7 +22,7 @@ export const statusesLoader = loader$<
const handle = parseHandle(accountId)
accountId = handle.localPart
const response = await getLocalStatuses(request as Request, platform.DATABASE, handle, 0, false)
const response = await getLocalStatuses(request as Request, await getDatabase(platform as any), handle, 0, false)
statuses = await response.json<Array<MastodonStatus>>()
} catch {
throw html(

Wyświetl plik

@ -1,4 +1,5 @@
import { component$, Slot, useStyles$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { type DocumentHead, loader$, useLocation, Link } from '@builder.io/qwik-city'
import { MastodonAccount } from 'wildebeest/backend/src/types'
import StickyHeader from '~/components/StickyHeader/StickyHeader'
@ -24,14 +25,14 @@ export const accountPageLoader = loader$<
const accountId = url.pathname.split('/')[1]
try {
const statusResponse = await statusAPI.handleRequestGet(platform.DATABASE, params.statusId, domain)
const statusResponse = await statusAPI.handleRequestGet(await getDatabase(platform as any), params.statusId, domain)
const statusText = await statusResponse.text()
isValidStatus = !!statusText
} catch {
isValidStatus = false
}
account = await getAccount(domain, accountId, platform.DATABASE)
account = await getAccount(domain, accountId, await getDatabase(platform as any))
} catch {
throw html(
500,

Wyświetl plik

@ -1,4 +1,5 @@
import { $, component$, useStyles$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { loader$ } from '@builder.io/qwik-city'
import styles from '../../../../utils/innerHtmlContent.scss?inline'
import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml'
@ -22,7 +23,7 @@ export const statusesLoader = loader$<
const handle = parseHandle(accountId)
accountId = handle.localPart
const response = await getLocalStatuses(request as Request, platform.DATABASE, handle, 0, true)
const response = await getLocalStatuses(request as Request, await getDatabase(platform as any), handle, 0, true)
statuses = await response.json<Array<MastodonStatus>>()
} catch {
throw html(

Wyświetl plik

@ -1,4 +1,5 @@
import { $, component$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
import * as timelines from 'wildebeest/functions/api/v1/timelines/public'
import { StatusesPanel } from '~/components/StatusesPanel/StatusesPanel'
@ -10,7 +11,7 @@ export const statusesLoader = loader$<Promise<MastodonStatus[]>, { DATABASE: D1D
async ({ platform, html }) => {
try {
// TODO: use the "trending" API endpoint here.
const response = await timelines.handleRequest(platform.domain, platform.DATABASE)
const response = await timelines.handleRequest(platform.domain, await getDatabase(platform as any))
const results = await response.text()
// Manually parse the JSON to ensure that Qwik finds the resulting objects serializable.
return JSON.parse(results) as MastodonStatus[]

Wyświetl plik

@ -1,4 +1,5 @@
import { $, component$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { MastodonStatus } from '~/types'
import * as timelines from 'wildebeest/functions/api/v1/timelines/public'
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
@ -11,7 +12,7 @@ export const statusesLoader = loader$<Promise<MastodonStatus[]>, { DATABASE: D1D
async ({ platform, html }) => {
try {
// TODO: use the "trending" API endpoint here.
const response = await timelines.handleRequest(platform.domain, platform.DATABASE)
const response = await timelines.handleRequest(platform.domain, await getDatabase(platform as any))
const results = await response.text()
// Manually parse the JSON to ensure that Qwik finds the resulting objects serializable.
return JSON.parse(results) as MastodonStatus[]

Wyświetl plik

@ -1,4 +1,5 @@
import { $, component$ } from '@builder.io/qwik'
import { getDatabase } from 'wildebeest/backend/src/database'
import { MastodonStatus } from '~/types'
import * as timelines from 'wildebeest/functions/api/v1/timelines/public'
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
@ -11,7 +12,7 @@ export const statusesLoader = loader$<Promise<MastodonStatus[]>, { DATABASE: D1D
async ({ platform, html }) => {
try {
// TODO: use the "trending" API endpoint here.
const response = await timelines.handleRequest(platform.domain, platform.DATABASE, { local: true })
const response = await timelines.handleRequest(platform.domain, await getDatabase(platform as any), { local: true })
const results = await response.text()
// Manually parse the JSON to ensure that Qwik finds the resulting objects serializable.
return JSON.parse(results) as MastodonStatus[]

Wyświetl plik

@ -7,7 +7,7 @@ import type { WebFingerResponse } from '../../backend/src/webfinger'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequest: PagesFunction<Env, any> = async ({ request, env }) => {
return handleRequest(request, getDatabase(env))
return handleRequest(request, await getDatabase(env))
}
const headers = {

Wyświetl plik

@ -5,7 +5,7 @@ import * as objects from 'wildebeest/backend/src/activitypub/objects'
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -7,7 +7,7 @@ import * as actors from 'wildebeest/backend/src/activitypub/actors'
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -11,7 +11,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
export async function handleRequest(domain: string, db: Database, id: string): Promise<Response> {

Wyświetl plik

@ -8,7 +8,7 @@ import type { Env } from 'wildebeest/backend/src/types/env'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -11,7 +11,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any> = async ({ params, request, env }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
export async function handleRequest(domain: string, db: Database, id: string): Promise<Response> {

Wyświetl plik

@ -8,7 +8,7 @@ import type { Env } from 'wildebeest/backend/src/types/env'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -41,7 +41,7 @@ export const onRequest: PagesFunction<Env, any> = async ({ params, request, env
const domain = new URL(request.url).hostname
return handleRequest(
domain,
getDatabase(env),
await getDatabase(env),
params.id as string,
activity,
env.QUEUE,

Wyświetl plik

@ -7,7 +7,7 @@ import type { Env } from 'wildebeest/backend/src/types/env'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string, env.userKEK)
return handleRequest(domain, await getDatabase(env), params.id as string, env.userKEK)
}
const headers = {

Wyświetl plik

@ -12,7 +12,7 @@ import { PUBLIC_GROUP } from 'wildebeest/backend/src/activitypub/activities'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -13,7 +13,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, params.id as string, getDatabase(env))
return handleRequest(domain, params.id as string, await getDatabase(env))
}
export async function handleRequest(domain: string, id: string, db: Database): Promise<Response> {

Wyświetl plik

@ -13,7 +13,7 @@ import type { Relationship } from 'wildebeest/backend/src/types/account'
import { addFollowing } from 'wildebeest/backend/src/mastodon/follow'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params, data }) => {
return handleRequest(request, getDatabase(env), params.id as string, data.connectedActor, env.userKEK)
return handleRequest(request, await getDatabase(env), params.id as string, data.connectedActor, env.userKEK)
}
export async function handleRequest(

Wyświetl plik

@ -16,7 +16,7 @@ import { getFollowers, loadActors } from 'wildebeest/backend/src/activitypub/act
import * as localFollow from 'wildebeest/backend/src/mastodon/follow'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ params, request, env }) => {
return handleRequest(request, getDatabase(env), params.id as string)
return handleRequest(request, await getDatabase(env), params.id as string)
}
export async function handleRequest(request: Request, db: Database, id: string): Promise<Response> {

Wyświetl plik

@ -16,7 +16,7 @@ import * as webfinger from 'wildebeest/backend/src/webfinger'
import { getFollowing, loadActors } from 'wildebeest/backend/src/activitypub/actors/follow'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ params, request, env }) => {
return handleRequest(request, getDatabase(env), params.id as string)
return handleRequest(request, await getDatabase(env), params.id as string)
}
export async function handleRequest(request: Request, db: Database, id: string): Promise<Response> {

Wyświetl plik

@ -26,7 +26,7 @@ const headers = {
}
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
return handleRequest(request, getDatabase(env), params.id as string)
return handleRequest(request, await getDatabase(env), params.id as string)
}
export async function handleRequest(request: Request, db: Database, id: string): Promise<Response> {
@ -140,10 +140,10 @@ FROM outbox_objects
INNER JOIN objects ON objects.id=outbox_objects.object_id
INNER JOIN actors ON actors.id=outbox_objects.actor_id
WHERE objects.type='Note'
${withReplies ? '' : "AND json_extract(objects.properties, '$.inReplyTo') IS NULL"}
${withReplies ? '' : 'AND ' + db.qb.jsonExtractIsNull('objects.properties', 'inReplyTo')}
AND outbox_objects.target = '${PUBLIC_GROUP}'
AND outbox_objects.actor_id = ?1
AND outbox_objects.cdate > ?2
AND outbox_objects.cdate > ?2${db.qb.psql('::timestamp')}
ORDER by outbox_objects.published_date DESC
LIMIT ?3 OFFSET ?4
`
@ -161,7 +161,7 @@ LIMIT ?3 OFFSET ?4
return new Response(JSON.stringify(out), { headers })
}
let afterCdate = '00-00-00 00:00:00'
let afterCdate = db.qb.epoch()
if (url.searchParams.has('max_id')) {
// Client asked to retrieve statuses after the max_id
// As opposed to Mastodon we don't use incremental ID but UUID, we need

Wyświetl plik

@ -12,7 +12,7 @@ import type { Relationship } from 'wildebeest/backend/src/types/account'
import { removeFollowing } from 'wildebeest/backend/src/mastodon/follow'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params, data }) => {
return handleRequest(request, getDatabase(env), params.id as string, data.connectedActor, env.userKEK)
return handleRequest(request, await getDatabase(env), params.id as string, data.connectedActor, env.userKEK)
}
export async function handleRequest(

Wyświetl plik

@ -8,7 +8,7 @@ import type { ContextData } from 'wildebeest/backend/src/types/context'
import { getFollowingAcct, getFollowingRequestedAcct } from 'wildebeest/backend/src/mastodon/follow'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
return handleRequest(request, getDatabase(env), data.connectedActor)
return handleRequest(request, await getDatabase(env), data.connectedActor)
}
export async function handleRequest(req: Request, db: Database, connectedActor: Person): Promise<Response> {

Wyświetl plik

@ -22,7 +22,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, data, env }) => {
return handleRequest(
getDatabase(env),
await getDatabase(env),
request,
data.connectedActor,
env.CF_ACCOUNT_ID,

Wyświetl plik

@ -12,7 +12,7 @@ export const onRequest: PagesFunction<Env, any, ContextData> = async ({ data, en
if (!data.connectedActor) {
return errors.notAuthorized('no connected user')
}
const user = await loadLocalMastodonAccount(getDatabase(env), data.connectedActor)
const user = await loadLocalMastodonAccount(await getDatabase(env), data.connectedActor)
const res: CredentialAccount = {
...user,

Wyświetl plik

@ -17,7 +17,7 @@ type AppsPost = {
}
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
return handleRequest(getDatabase(env), request, getVAPIDKeys(env))
return handleRequest(await getDatabase(env), request, getVAPIDKeys(env))
}
export async function handleRequest(db: Database, request: Request, vapidKeys: JWK) {

Wyświetl plik

@ -1,5 +1,6 @@
// https://docs.joinmastodon.org/methods/apps/#verify_credentials
import { type Database } from 'wildebeest/backend/src/database'
import { cors } from 'wildebeest/backend/src/utils/cors'
import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
import { getVAPIDKeys } from 'wildebeest/backend/src/config'
@ -24,7 +25,7 @@ export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request,
return handleRequest(env.DATABASE, request, getVAPIDKeys(env))
}
export async function handleRequest(db: D1Database, request: Request, vapidKeys: JWK) {
export async function handleRequest(db: Database, request: Request, vapidKeys: JWK) {
if (request.method !== 'GET') {
return new Response('', { status: 400 })
}

Wyświetl plik

@ -4,7 +4,7 @@ import type { Env } from 'wildebeest/backend/src/types/env'
import { getPeers } from 'wildebeest/backend/src/activitypub/peers'
export const onRequest: PagesFunction<Env, any> = async ({ env }) => {
return handleRequest(getDatabase(env))
return handleRequest(await getDatabase(env))
}
export async function handleRequest(db: Database): Promise<Response> {

Wyświetl plik

@ -15,7 +15,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ data, request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, params.id as string, getDatabase(env), data.connectedActor)
return handleRequest(domain, params.id as string, await getDatabase(env), data.connectedActor)
}
export async function handleRequest(

Wyświetl plik

@ -12,11 +12,11 @@ import { VAPIDPublicKey } from 'wildebeest/backend/src/mastodon/subscription'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
return handleGetRequest(getDatabase(env), request, data.connectedActor, data.clientId, getVAPIDKeys(env))
return handleGetRequest(await getDatabase(env), request, data.connectedActor, data.clientId, getVAPIDKeys(env))
}
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
return handlePostRequest(getDatabase(env), request, data.connectedActor, data.clientId, getVAPIDKeys(env))
return handlePostRequest(await getDatabase(env), request, data.connectedActor, data.clientId, getVAPIDKeys(env))
}
const headers = {

Wyświetl plik

@ -39,7 +39,7 @@ type StatusCreate = {
}
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
return handleRequest(request, getDatabase(env), data.connectedActor, env.userKEK, env.QUEUE, cacheFromEnv(env))
return handleRequest(request, await getDatabase(env), data.connectedActor, env.userKEK, env.QUEUE, cacheFromEnv(env))
}
// FIXME: add tests for delivery to followers and mentions to a specific Actor.

Wyświetl plik

@ -20,13 +20,13 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ params, env, request, data }) => {
const domain = new URL(request.url).hostname
return handleRequestGet(getDatabase(env), params.id as UUID, domain, data.connectedActor)
return handleRequestGet(await getDatabase(env), params.id as UUID, domain, data.connectedActor)
}
export const onRequestDelete: PagesFunction<Env, any, ContextData> = async ({ params, env, request, data }) => {
const domain = new URL(request.url).hostname
return handleRequestDelete(
getDatabase(env),
await getDatabase(env),
params.id as UUID,
data.connectedActor,
domain,

Wyświetl plik

@ -10,7 +10,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), params.id as string)
return handleRequest(domain, await getDatabase(env), params.id as string)
}
const headers = {

Wyświetl plik

@ -17,7 +17,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ env, data, params, request }) => {
const domain = new URL(request.url).hostname
return handleRequest(getDatabase(env), params.id as string, data.connectedActor, env.userKEK, domain)
return handleRequest(await getDatabase(env), params.id as string, data.connectedActor, env.userKEK, domain)
}
export async function handleRequest(

Wyświetl plik

@ -17,7 +17,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ env, data, params, request }) => {
const domain = new URL(request.url).hostname
return handleRequest(getDatabase(env), params.id as string, data.connectedActor, env.userKEK, env.QUEUE, domain)
return handleRequest(await getDatabase(env), params.id as string, data.connectedActor, env.userKEK, env.QUEUE, domain)
}
export async function handleRequest(

Wyświetl plik

@ -14,7 +14,7 @@ const headers = {
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ params, env, request }) => {
const domain = new URL(request.url).hostname
return handleRequestGet(getDatabase(env), domain, params.tag as string)
return handleRequestGet(await getDatabase(env), domain, params.tag as string)
}
export async function handleRequestGet(db: Database, domain: string, value: string): Promise<Response> {

Wyświetl plik

@ -16,7 +16,7 @@ export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request,
const only_media = searchParams.get('only_media') === 'true'
const offset = Number.parseInt(searchParams.get('offset') ?? '0')
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), { local, remote, only_media, offset })
return handleRequest(domain, await getDatabase(env), { local, remote, only_media, offset })
}
export async function handleRequest(

Wyświetl plik

@ -11,7 +11,7 @@ const headers = {
export const onRequest: PagesFunction<Env, any, ContextData> = async ({ request, env, params }) => {
const domain = new URL(request.url).hostname
return handleRequest(getDatabase(env), request, domain, params.tag as string)
return handleRequest(await getDatabase(env), request, domain, params.tag as string)
}
export async function handleRequest(db: Database, request: Request, domain: string, tag: string): Promise<Response> {

Wyświetl plik

@ -7,7 +7,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequest: PagesFunction<Env, any> = async ({ env, request }) => {
const domain = new URL(request.url).hostname
return handleRequest(domain, getDatabase(env), env)
return handleRequest(domain, await getDatabase(env), env)
}
export async function handleRequest(domain: string, db: Database, env: Env) {

Wyświetl plik

@ -9,7 +9,7 @@ import { mastodonIdSymbol } from 'wildebeest/backend/src/activitypub/objects'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env, data }) => {
return handleRequestPost(request, getDatabase(env), data.connectedActor, env.CF_ACCOUNT_ID, env.CF_API_TOKEN)
return handleRequestPost(request, await getDatabase(env), data.connectedActor, env.CF_ACCOUNT_ID, env.CF_API_TOKEN)
}
export async function handleRequestPost(

Wyświetl plik

@ -14,7 +14,7 @@ import { updateObjectProperty } from 'wildebeest/backend/src/activitypub/objects
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestPut: PagesFunction<Env, any, ContextData> = async ({ params, env, request }) => {
return handleRequestPut(getDatabase(env), params.id as UUID, request)
return handleRequestPut(await getDatabase(env), params.id as UUID, request)
}
type UpdateMedia = {

Wyświetl plik

@ -22,7 +22,7 @@ type SearchResult = {
}
export const onRequest: PagesFunction<Env, any> = async ({ request, env }) => {
return handleRequest(getDatabase(env), request)
return handleRequest(await getDatabase(env), request)
}
export async function handleRequest(db: Database, request: Request): Promise<Response> {

Wyświetl plik

@ -8,7 +8,7 @@ import * as errors from 'wildebeest/backend/src/errors'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ env, request, data }) => {
return handleRequestPost(getDatabase(env), request, data.connectedActor)
return handleRequestPost(await getDatabase(env), request, data.connectedActor)
}
type AddAliasRequest = {

Wyświetl plik

@ -9,7 +9,7 @@ import * as access from 'wildebeest/backend/src/access'
import { type Database, getDatabase } from 'wildebeest/backend/src/database'
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
return handlePostRequest(request, getDatabase(env), env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
return handlePostRequest(request, await getDatabase(env), env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
}
export async function handlePostRequest(

Wyświetl plik

@ -13,7 +13,7 @@ import { type Database, getDatabase } from 'wildebeest/backend/src/database'
const extractJWTFromRequest = (request: Request) => request.headers.get('Cf-Access-Jwt-Assertion') || ''
export const onRequestPost: PagesFunction<Env, any, ContextData> = async ({ request, env }) => {
return handleRequestPost(request, getDatabase(env), env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
return handleRequestPost(request, await getDatabase(env), env.userKEK, env.ACCESS_AUTH_DOMAIN, env.ACCESS_AUD)
}
export async function buildRedirect(

Wyświetl plik

@ -12,7 +12,7 @@ type Body = {
}
export const onRequest: PagesFunction<Env, any> = async ({ request, env }) => {
return handleRequest(getDatabase(env), request)
return handleRequest(await getDatabase(env), request)
}
export async function handleRequest(db: Database, request: Request): Promise<Response> {

13
functions/test.ts 100644
Wyświetl plik

@ -0,0 +1,13 @@
import type { Env } from 'wildebeest/backend/src/types/env'
import { getDatabase } from 'wildebeest/backend/src/database'
import type { ContextData } from 'wildebeest/backend/src/types/context'
import * as neon from '@neondatabase/serverless'
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ env }) => {
const client = new neon.Client(env.NEON_DATABASE_URL!)
console.log(env.NEON_DATABASE_URL!)
await client.connect()
const res = await client.query('select 2')
return new Response(JSON.stringify(res))
}

Wyświetl plik

@ -11,6 +11,7 @@
"@playwright/test": "^1.30.0",
"@types/jest": "^29.2.4",
"@types/node": "^18.11.11",
"@types/pg": "^8.6.6",
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
"better-sqlite3": "8",
@ -44,9 +45,12 @@
"deploy": "yarn build && yarn database:migrate && yarn pages publish frontend/dist --project-name=wildebeest"
},
"dependencies": {
"@neondatabase/serverless": "^0.2.5",
"@planetscale/database": "^1.5.0",
"@types/cookie": "^0.5.1",
"cookie": "^0.5.0",
"http-message-signatures": "^0.1.2",
"pg": "^8.9.0",
"toucan-js": "^3.1.0"
}
}

165
postgresql.sql 100644
Wyświetl plik

@ -0,0 +1,165 @@
-- Migration number: 0000 2022-12-05T20:27:34.391Z
CREATE TABLE IF NOT EXISTS actors (
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
email TEXT,
privkey bytea,
privkey_salt bytea,
pubkey TEXT,
cdate timestamp NOT NULL DEFAULT (now()),
properties TEXT NOT NULL DEFAULT ('{}')
);
CREATE INDEX IF NOT EXISTS actors_email ON actors(email);
CREATE TABLE IF NOT EXISTS actor_following (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
target_actor_id TEXT NOT NULL,
target_actor_acct TEXT NOT NULL,
state TEXT NOT NULL DEFAULT 'pending',
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE INDEX IF NOT EXISTS actor_following_actor_id ON actor_following(actor_id);
CREATE INDEX IF NOT EXISTS actor_following_target_actor_id ON actor_following(target_actor_id);
CREATE TABLE IF NOT EXISTS objects (
id TEXT PRIMARY KEY,
mastodon_id TEXT UNIQUE NOT NULL,
type TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now()),
original_actor_id TEXT,
original_object_id TEXT UNIQUE,
reply_to_object_id TEXT,
properties TEXT NOT NULL DEFAULT ('{}'),
local INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS inbox_objects (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE TABLE IF NOT EXISTS outbox_objects (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now()),
published_date timestamp NOT NULL DEFAULT (now())
);
CREATE TABLE IF NOT EXISTS actor_notifications (
id SERIAL PRIMARY KEY,
type TEXT NOT NULL,
actor_id TEXT NOT NULL,
from_actor_id TEXT NOT NULL,
object_id TEXT,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE INDEX IF NOT EXISTS actor_notifications_actor_id ON actor_notifications(actor_id);
CREATE TABLE IF NOT EXISTS actor_favourites (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE INDEX IF NOT EXISTS actor_favourites_actor_id ON actor_favourites(actor_id);
CREATE INDEX IF NOT EXISTS actor_favourites_object_id ON actor_favourites(object_id);
CREATE TABLE IF NOT EXISTS actor_reblogs (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE INDEX IF NOT EXISTS actor_reblogs_actor_id ON actor_reblogs(actor_id);
CREATE INDEX IF NOT EXISTS actor_reblogs_object_id ON actor_reblogs(object_id);
CREATE TABLE IF NOT EXISTS clients (
id TEXT PRIMARY KEY,
secret TEXT NOT NULL,
name TEXT NOT NULL,
redirect_uris TEXT NOT NULL,
website TEXT,
scopes TEXT,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE TABLE IF NOT EXISTS actor_replies (
id TEXT PRIMARY KEY,
actor_id TEXT NOT NULL,
object_id TEXT NOT NULL,
in_reply_to_object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now())
);
CREATE INDEX IF NOT EXISTS actor_replies_in_reply_to_object_id ON actor_replies(in_reply_to_object_id);
-- Migration number: 0001 2023-01-16T13:09:04.033Z
CREATE UNIQUE INDEX unique_actor_following ON actor_following (actor_id, target_actor_id);
-- Migration number: 0002 2023-01-16T13:46:54.975Z
ALTER TABLE outbox_objects
ADD target TEXT NOT NULL DEFAULT 'https://www.w3.org/ns/activitystreams#Public';
-- Migration number: 0003 2023-02-02T15:03:27.478Z
CREATE TABLE IF NOT EXISTS peers (
domain TEXT UNIQUE NOT NULL
);
-- Migration number: 0004 2023-02-03T17:17:19.099Z
CREATE INDEX IF NOT EXISTS outbox_objects_actor_id ON outbox_objects(actor_id);
CREATE INDEX IF NOT EXISTS outbox_objects_target ON outbox_objects(target);
-- Migration number: 0005 2023-02-07T10:57:21.848Z
CREATE TABLE IF NOT EXISTS idempotency_keys (
key TEXT PRIMARY KEY,
object_id TEXT NOT NULL,
expires_at timestamp NOT NULL
);
-- Migration number: 0006 2023-02-13T11:18:03.485Z
CREATE TABLE IF NOT EXISTS note_hashtags (
value TEXT NOT NULL,
object_id TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now())
);
-- Migration number: 0007 2023-02-15T11:01:46.585Z
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
actor_id TEXT NOT NULL,
client_id TEXT NOT NULL,
endpoint TEXT NOT NULL,
key_p256dh TEXT NOT NULL,
key_auth TEXT NOT NULL,
alert_mention INTEGER NOT NULL,
alert_status INTEGER NOT NULL,
alert_reblog INTEGER NOT NULL,
alert_follow INTEGER NOT NULL,
alert_follow_request INTEGER NOT NULL,
alert_favourite INTEGER NOT NULL,
alert_poll INTEGER NOT NULL,
alert_update INTEGER NOT NULL,
alert_admin_sign_up INTEGER NOT NULL,
alert_admin_report INTEGER NOT NULL,
policy TEXT NOT NULL,
cdate timestamp NOT NULL DEFAULT (now()),
UNIQUE(actor_id, client_id)
);

Wyświetl plik

@ -101,7 +101,7 @@ resource "random_password" "user_key" {
resource "cloudflare_pages_project" "wildebeest_pages_project" {
account_id = var.cloudflare_account_id
name = "wildebeest-${lower(var.name_suffix)}"
production_branch = "main"
production_branch = "sven/neon"
deployment_configs {
production {

180
yarn.lock
Wyświetl plik

@ -316,12 +316,12 @@
"@databases/split-sql-query@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@databases/split-sql-query/-/split-sql-query-1.0.3.tgz#de2a14b16be14769b79de77e9b62f4a4aded13f9"
resolved "https://registry.npmjs.org/@databases/split-sql-query/-/split-sql-query-1.0.3.tgz"
integrity sha512-Q3UYX85e34yE9KXa095AJtJhBQ0NpLfC0kS9ydFKuNB25cto4YddY52RuXN81m2t0pS1Atg31ylNpKfNCnUPdA==
"@databases/sql@3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@databases/sql/-/sql-3.2.0.tgz#ac55ddaed5408c98e3891362ac5b70d06d7a89ff"
resolved "https://registry.npmjs.org/@databases/sql/-/sql-3.2.0.tgz"
integrity sha512-xQZzKIa0lvcdo0MYxnyFMVS1TRla9lpDSCYkobJl19vQEOJ9TqE4o8QBGRJNUfhSkbQIWyvMeBl3KBBbqyUVQQ==
"@esbuild-plugins/node-globals-polyfill@^0.1.1":
@ -636,7 +636,7 @@
"@miniflare/cli-parser@2.11.0":
version "2.11.0"
resolved "https://registry.yarnpkg.com/@miniflare/cli-parser/-/cli-parser-2.11.0.tgz#47f517731791c9e652e9849d590fde3235737529"
resolved "https://registry.npmjs.org/@miniflare/cli-parser/-/cli-parser-2.11.0.tgz"
integrity sha512-JUmyRzEGAS6CouvXJwBh8p44onfw3KRpfq5JGXEuHModOGjTp6li7PQyCTNPV2Hv/7StAXWnTFGXeAqyDHuTig==
dependencies:
"@miniflare/shared" "2.11.0"
@ -688,7 +688,7 @@
"@miniflare/http-server@2.11.0":
version "2.11.0"
resolved "https://registry.yarnpkg.com/@miniflare/http-server/-/http-server-2.11.0.tgz#76d2e2c6549528d965e5f48a8ddc3448c28d4569"
resolved "https://registry.npmjs.org/@miniflare/http-server/-/http-server-2.11.0.tgz"
integrity sha512-sMLcrDFzqqAvnQmAUH0hRTo8sBjW79VZYfnIH5FAGSGcKX6kdAGs9RStdYZ4CftQCBAEQScX0KBsMx5FwJRe9Q==
dependencies:
"@miniflare/core" "2.11.0"
@ -731,7 +731,7 @@
"@miniflare/scheduler@2.11.0":
version "2.11.0"
resolved "https://registry.yarnpkg.com/@miniflare/scheduler/-/scheduler-2.11.0.tgz#2568d44f571e73355369be6a6da4481aa4af25c8"
resolved "https://registry.npmjs.org/@miniflare/scheduler/-/scheduler-2.11.0.tgz"
integrity sha512-DPdzINhdWeS99eIicGoluMsD4pLTTAWNQbgCv3CTwgdKA3dxdvMSCkNqZzQLiALzvk9+rSfj46FlH++HE7o7/w==
dependencies:
"@miniflare/core" "2.11.0"
@ -807,6 +807,11 @@
undici "5.9.1"
ws "^8.2.2"
"@neondatabase/serverless@^0.2.5":
version "0.2.5"
resolved "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.2.5.tgz"
integrity sha512-Qu/nNZftfoqw4ojVCXU/EgYlfII3mzLm82iXNOUljFumPhoZ/Wp8NJG5DgSAKCWC0zwTyJsojdPLQDj/UPs2vg==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@ -828,9 +833,14 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@planetscale/database@^1.5.0":
version "1.5.0"
resolved "https://registry.npmjs.org/@planetscale/database/-/database-1.5.0.tgz"
integrity sha512-Qwh7Or1W5dB5mZ9EQqDkgvkDKhBBmQe58KIVUy0SGocNtr5fP4JAWtvZ6EdLAV6C6hVpzNlCA2xIg9lKTswm1Q==
"@playwright/test@^1.30.0":
version "1.30.0"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.30.0.tgz#8c0c4930ff2c7be7b3ec3fd434b2a3b4465ed7cb"
resolved "https://registry.npmjs.org/@playwright/test/-/test-1.30.0.tgz"
integrity sha512-SVxkQw1xvn/Wk/EvBnqWIq6NLo1AppwbYOjNLmyU0R1RoQ3rLEBtmjTnElcnz8VEtn11fptj1ECxK0tgURhajw==
dependencies:
"@types/node" "*"
@ -838,7 +848,7 @@
"@sentry/core@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e"
resolved "https://registry.npmjs.org/@sentry/core/-/core-7.28.1.tgz"
integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==
dependencies:
"@sentry/types" "7.28.1"
@ -847,12 +857,12 @@
"@sentry/types@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d"
resolved "https://registry.npmjs.org/@sentry/types/-/types-7.28.1.tgz"
integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==
"@sentry/utils@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e"
resolved "https://registry.npmjs.org/@sentry/utils/-/utils-7.28.1.tgz"
integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==
dependencies:
"@sentry/types" "7.28.1"
@ -919,7 +929,7 @@
"@types/cookie@^0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.1.tgz#b29aa1f91a59f35e29ff8f7cb24faf1a3a750554"
resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.1.tgz"
integrity sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==
"@types/graceful-fs@^4.1.3":
@ -966,6 +976,15 @@
resolved "https://registry.npmjs.org/@types/node/-/node-18.11.11.tgz"
integrity sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==
"@types/pg@^8.6.6":
version "8.6.6"
resolved "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz"
integrity sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==
dependencies:
"@types/node" "*"
pg-protocol "*"
pg-types "^2.2.0"
"@types/prettier@^2.1.5":
version "2.7.1"
resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz"
@ -1159,7 +1178,7 @@ array-union@^2.1.0:
available-typed-arrays@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
babel-jest@^29.3.1:
@ -1310,6 +1329,11 @@ buffer-from@^1.0.0:
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-writer@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz"
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
buffer@^5.5.0:
version "5.7.1"
resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz"
@ -1334,7 +1358,7 @@ busboy@^1.6.0:
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
@ -2520,21 +2544,21 @@ is-stream@^3.0.0:
is-string@^1.0.5, is-string@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
dependencies:
has-tostringtag "^1.0.0"
is-symbol@^1.0.2, is-symbol@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz"
integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
dependencies:
has-symbols "^1.0.2"
is-typed-array@^1.1.10, is-typed-array@^1.1.9:
version "1.1.10"
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz"
integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
dependencies:
available-typed-arrays "^1.0.5"
@ -2545,7 +2569,7 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9:
is-weakref@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz"
integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
dependencies:
call-bind "^1.0.2"
@ -3173,7 +3197,7 @@ mimic-response@^3.1.0:
miniflare@2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-2.11.0.tgz#36c575e1e75451c416f136d188b744896becc352"
resolved "https://registry.npmjs.org/miniflare/-/miniflare-2.11.0.tgz"
integrity sha512-QA18I1VQXdCo4nBtPJUcUDxW8c9xbc5ex5F61jwhkGVOISSnYdEheolESmjr8MYk28xwi0XD1ozS4rLaTONd+w==
dependencies:
"@miniflare/cache" "2.11.0"
@ -3247,7 +3271,7 @@ natural-compare@^1.4.0:
nice-try@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-abi@^3.3.0:
@ -3274,7 +3298,7 @@ node-releases@^2.0.6:
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
@ -3289,7 +3313,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
npm-run-all@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
resolved "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz"
integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==
dependencies:
ansi-styles "^3.2.1"
@ -3328,17 +3352,17 @@ npx-import@^1.1.3:
object-inspect@^1.12.2, object-inspect@^1.9.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz"
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
dependencies:
call-bind "^1.0.2"
@ -3412,6 +3436,11 @@ p-try@^2.0.0:
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
packet-reader@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz"
integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
@ -3421,7 +3450,7 @@ parent-module@^1.0.0:
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz"
integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==
dependencies:
error-ex "^1.3.1"
@ -3489,6 +3518,57 @@ path-type@^4.0.0:
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
pg-connection-string@^2.5.0:
version "2.5.0"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz"
integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
pg-pool@^3.5.2:
version "3.5.2"
resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz"
integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==
pg-protocol@*, pg-protocol@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz"
integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==
pg-types@^2.1.0, pg-types@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz"
integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
dependencies:
pg-int8 "1.0.1"
postgres-array "~2.0.0"
postgres-bytea "~1.0.0"
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@^8.9.0:
version "8.9.0"
resolved "https://registry.npmjs.org/pg/-/pg-8.9.0.tgz"
integrity sha512-ZJM+qkEbtOHRuXjmvBtOgNOXOtLSbxiMiUVMgE4rV6Zwocy03RicCVvDXgx8l4Biwo8/qORUnEqn2fdQzV7KCg==
dependencies:
buffer-writer "2.0.0"
packet-reader "1.0.0"
pg-connection-string "^2.5.0"
pg-pool "^3.5.2"
pg-protocol "^1.6.0"
pg-types "^2.1.0"
pgpass "1.x"
pgpass@1.x:
version "1.0.5"
resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz"
integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==
dependencies:
split2 "^4.1.0"
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
@ -3526,6 +3606,28 @@ playwright-core@1.30.0:
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285"
integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==
postgres-array@~2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz"
integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
postgres-bytea@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz"
integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==
postgres-date@~1.0.4:
version "1.0.7"
resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz"
integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
postgres-interval@^1.1.0:
version "1.2.0"
resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz"
integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
dependencies:
xtend "^4.0.0"
prebuild-install@^7.1.0:
version "7.1.1"
resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz"
@ -3905,6 +4007,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779"
integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==
split2@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz"
integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
@ -4095,7 +4202,7 @@ to-regex-range@^5.0.1:
toucan-js@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/toucan-js/-/toucan-js-3.1.0.tgz#412cf43c259e702f46427e465adb2f588b7eea85"
resolved "https://registry.npmjs.org/toucan-js/-/toucan-js-3.1.0.tgz"
integrity sha512-bRbq/HB+aSfwbsSoCNI6qyPXx2bhsscxSYxnAY63xXv9lIeOLUYfvYdOIBWfAVj9QHNST+X83GQ0lj/llvHpVg==
dependencies:
"@sentry/core" "7.28.1"
@ -4104,7 +4211,7 @@ toucan-js@^3.1.0:
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
ts-jest@^29.0.3:
@ -4128,7 +4235,7 @@ tslib@^1.8.1, tslib@^1.9.3:
tslib@^2.1.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tsutils@^3.21.0:
@ -4169,7 +4276,7 @@ type-fest@^0.21.3:
typed-array-length@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz"
integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==
dependencies:
call-bind "^1.0.2"
@ -4183,7 +4290,7 @@ typescript@^4.9.4:
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
dependencies:
call-bind "^1.0.2"
@ -4232,7 +4339,7 @@ v8-to-istanbul@^9.0.1:
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz"
integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
dependencies:
spdx-correct "^3.0.0"
@ -4254,7 +4361,7 @@ walker@^1.0.8:
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
dependencies:
is-bigint "^1.0.1"
@ -4265,7 +4372,7 @@ which-boxed-primitive@^1.0.2:
which-typed-array@^1.1.9:
version "1.1.9"
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz"
integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
dependencies:
available-typed-arrays "^1.0.5"
@ -4277,7 +4384,7 @@ which-typed-array@^1.1.9:
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
@ -4296,7 +4403,7 @@ word-wrap@^1.2.3:
wrangler@2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/wrangler/-/wrangler-2.7.1.tgz#adb9d06c0dde3fc434e4a85a286c1da3f485cade"
resolved "https://registry.npmjs.org/wrangler/-/wrangler-2.7.1.tgz"
integrity sha512-SKoe+UTOCX0J+RfEDE6MEdnNy1lDSnB1BfpAEblgvDHjmEunPFu0w6GxcyOYAl/fTl3/VjXZO3p+Ybm9zenzWg==
dependencies:
"@cloudflare/kv-asset-handler" "^0.2.0"
@ -4344,6 +4451,11 @@ ws@^8.2.2:
resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
xtend@^4.0.0:
version "4.0.2"
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
xxhash-wasm@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.0.2.tgz"