chore: perauset customer project

This commit is contained in:
e2e
2026-07-11 08:35:17 +02:00
commit 37eea09bf0
293 changed files with 28412 additions and 0 deletions

54
backend/bin/db-migrate.ts Normal file
View File

@@ -0,0 +1,54 @@
import pg from 'pg'
import { migrate } from 'postgres-migrations'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const dbConfig: string | pg.ClientConfig = process.env.DATABASE_URL
? process.env.DATABASE_URL
: {
host: process.env.DB_HOST ?? '0.0.0.0',
port: Number(process.env.DB_PORT ?? 5432),
user: process.env.DB_USER ?? 'yasser',
password: process.env.DB_PASSWORD ?? '',
database: process.env.DB_NAME ?? 'perauset',
}
async function main() {
// Create database if it doesn't exist (skip when using connection string — DB is pre-provisioned)
if (typeof dbConfig !== 'string') {
const client = new pg.Client({ ...dbConfig, database: 'postgres' })
try {
await client.connect()
if (process.env.RESET_DB === 'true') {
console.log('Dropping database...')
await client.query(`DROP DATABASE IF EXISTS "${dbConfig.database}"`)
}
console.log(`Creating database "${dbConfig.database}"...`)
await client.query(`CREATE DATABASE "${dbConfig.database}"`)
} catch (e: any) {
if (e.code === '42P04') {
console.log('Database already exists')
} else {
throw e
}
} finally {
await client.end()
}
}
// Run migrations
const client = new pg.Client(dbConfig as any)
await client.connect()
console.log('Running migrations...')
await migrate({ client }, `${__dirname}/../../sql`, { logger: undefined })
console.log('Migrations complete')
await client.end()
}
main().catch((e) => {
console.error('Migration failed:', e)
process.exit(1)
})

73
backend/bin/start-e2e.ts Normal file
View File

@@ -0,0 +1,73 @@
// Isolated SQLite backend for e2e tests.
//
// The shipped runtime (Pikku Fabric) serves the API with an injected SQLite
// kysely built via `createSQLiteKysely`. The standalone `start.ts` uses the
// libsql *web* dialect, which only talks to a remote libsql server — it can't
// open a local file. This entry mirrors the Fabric runtime instead: it opens the
// local better-sqlite3 database (provisioned by `pikku db reset` in the e2e
// hooks) and injects the resulting kysely into `createSingletonServices` so
// better-auth (account/session tables) works fully offline and per-run isolated.
//
// Env:
// E2E_DB_FILE path to the provisioned sqlite file to open
// PORT http port (default 6099)
// BETTER_AUTH_SECRET better-auth signing secret (default e2e-test-secret)
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import Database from 'better-sqlite3'
import { Kysely, SqliteDialect, CamelCasePlugin } from 'kysely'
import { SerializePlugin } from '@pikku/kysely'
import { PikkuFastifyServer } from '@pikku/fastify'
import type { DB } from '@perauset/functions/.pikku/db/schema.js'
import { createConfig } from '@perauset/functions/src/config.js'
import { createSingletonServices } from '@perauset/functions/src/services.js'
import '@perauset/functions/.pikku/pikku-bootstrap.gen.js'
import '@perauset/functions/src/middleware.js'
import '@perauset/functions/src/wirings/auth.wiring.js'
import '@perauset/functions/src/wirings/http.wiring.js'
import '@perauset/functions/src/wirings/http-phase1b.wiring.js'
import '@perauset/functions/src/wirings/http-phase2.wiring.js'
import '@perauset/functions/src/lib/permissions.js'
const __dirname = dirname(fileURLToPath(import.meta.url))
const projectDir = resolve(__dirname, '../..')
function openDatabase(dbFile: string): Database.Database {
// The db file is provisioned by `pikku db reset` (migrations + sqlite-seed.sql)
// before this process starts — see the e2e BeforeAll hooks. Here we only open it.
const db = new Database(dbFile)
db.pragma('journal_mode = WAL')
db.pragma('foreign_keys = ON')
return db
}
async function main(): Promise<void> {
try {
process.env.BETTER_AUTH_SECRET ??= 'e2e-test-secret'
const dbFile = process.env.E2E_DB_FILE ?? resolve(projectDir, '.pikku-runtime/e2e.db')
const db = openDatabase(dbFile)
// Mirror the Fabric runtime's kysely: SerializePlugin for JSON columns +
// CamelCasePlugin so camelCase model fields (and better-auth's userId etc.)
// map to the snake_case columns in db/sqlite/*.sql.
const kysely = new Kysely<DB>({
dialect: new SqliteDialect({ database: db }),
plugins: [new SerializePlugin(), new CamelCasePlugin()],
})
const config = await createConfig()
const singletonServices = await createSingletonServices(config, { kysely })
const appServer = new PikkuFastifyServer(config, singletonServices.logger)
appServer.enableExitOnSigInt()
await appServer.init({ exposeErrors: true })
await appServer.start()
} catch (e: any) {
console.error(e.stack ?? e.toString())
process.exit(1)
}
}
main()

29
backend/bin/start.ts Normal file
View File

@@ -0,0 +1,29 @@
import { PikkuFastifyServer } from "@pikku/fastify"
import { createConfig } from "@perauset/functions/src/config.js"
import { createSingletonServices } from "@perauset/functions/src/services.js"
import "@perauset/functions/.pikku/pikku-bootstrap.gen.js"
import "@perauset/functions/src/middleware.js"
import "@perauset/functions/src/wirings/auth.wiring.js"
import "@perauset/functions/src/wirings/http.wiring.js"
import "@perauset/functions/src/wirings/http-phase1b.wiring.js"
import "@perauset/functions/src/wirings/http-phase2.wiring.js"
import "@perauset/functions/src/lib/permissions.js"
async function main(): Promise<void> {
try {
const config = await createConfig()
const singletonServices = await createSingletonServices(config)
const appServer = new PikkuFastifyServer(config, singletonServices.logger)
appServer.enableExitOnSigInt()
await appServer.init({ exposeErrors: true })
await appServer.start()
} catch (e: any) {
console.error(e.toString())
process.exit(1)
}
}
main()

33
backend/package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "@perauset/server",
"version": "0.0.0",
"description": "Perauset backend server",
"license": "MIT",
"private": true,
"type": "module",
"main": "bin/start.ts",
"scripts": {
"tsc": "tsc",
"start": "tsx bin/start.ts",
"dev": "tsx watch --env-file=../.env bin/start.ts",
"dbmigrate": "tsx --env-file=../.env bin/db-migrate.ts"
},
"dependencies": {
"@perauset/functions": "workspace:*",
"@pikku/core": "^0.12.57",
"@pikku/fastify": "^0.12.4",
"@pikku/kysely": "^0.13.0",
"kysely": "^0.29.2",
"pg": "^8.16.0",
"postgres-migrations": "^5.3.0",
"tslib": "^2.8.1",
"tsx": "^4.21.0",
"typescript": "^5.9"
},
"devDependencies": {
"@pikku/cli": "^0.12.76",
"@types/node": "^25",
"@types/pg": "^8",
"better-sqlite3": "^12.11.1"
}
}

16
backend/tsconfig.json Normal file
View File

@@ -0,0 +1,16 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": "..",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"types": ["node"],
"noEmit": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"files": [],
"include": ["src/*.ts", "src/**/*.ts", "bin/**/*.ts"],
"exclude": ["node_modules", "dist", "../packages/functions/.pikku"]
}