mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-15 10:43:07 +00:00
Collection contentEvent (hidden, sem hooks, sem PII) + beacon público POST /api/content-events fail-soft, download contado no servidor na rota de mídia, agregação em uma query group-by e a coluna "Circulação" na lista interna + painel na ficha (port classe-a-classe do artefato estendido, crítica final do designer certificada).
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import 'server-only'
|
||
|
||
import { sql } from '@payloadcms/db-postgres'
|
||
import type { Payload } from 'payload'
|
||
|
||
export type DrizzleTables = Record<string, Record<string, unknown>>
|
||
|
||
type PayloadDbTables = {
|
||
tables: DrizzleTables
|
||
}
|
||
|
||
/** The raw-SQL executor Payload's Postgres adapter exposes (drizzle session). */
|
||
export type PayloadDrizzle = {
|
||
execute: (query: ReturnType<typeof sql>) => Promise<unknown>
|
||
}
|
||
|
||
/**
|
||
* The drizzle session of the Postgres adapter, or null when the database is
|
||
* not PostgreSQL or the session is unavailable. Single owner of the
|
||
* `payload.db` narrowing the raw-SQL readers share; callers decide whether
|
||
* that is an error (throw) or a degraded read (fail soft).
|
||
*/
|
||
export const getPayloadDrizzle = (payload: Pick<Payload, 'db'>): PayloadDrizzle | null => {
|
||
if (payload.db.name !== 'postgres') return null
|
||
|
||
const database = payload.db as unknown as { drizzle?: PayloadDrizzle }
|
||
return database.drizzle && typeof database.drizzle.execute === 'function'
|
||
? database.drizzle
|
||
: null
|
||
}
|
||
|
||
/** ~15-16 columns × 500 ≈ 7500-8000 bind params — well under PG's 65535 param limit. */
|
||
export const INSERT_CHUNK_SIZE = 500
|
||
|
||
export const chunk = <T>(rows: readonly T[], size: number): T[][] => {
|
||
const out: T[][] = []
|
||
for (let i = 0; i < rows.length; i += size) out.push(rows.slice(i, i + size))
|
||
return out
|
||
}
|
||
|
||
export const getDrizzleTables = (payload: Pick<Payload, 'db'>): DrizzleTables =>
|
||
(payload.db as unknown as PayloadDbTables).tables
|
||
|
||
export const requireTable = (tables: DrizzleTables, name: string): Record<string, unknown> => {
|
||
const table = tables[name]
|
||
if (!table) throw new Error(`Tabela drizzle ausente: ${name}. Rode as migrations.`)
|
||
return table
|
||
}
|
||
|
||
/**
|
||
* Fails fast when a drizzle table's JS-level column scaffold has drifted from
|
||
* what a bulk insert assumes (e.g. `contactId` vs `contact`) — catches the bug
|
||
* class before a silent bad insert instead of a runtime SQL error mid-import.
|
||
*/
|
||
export const assertDrizzleColumns = (
|
||
table: Record<string, unknown>,
|
||
tableName: string,
|
||
expectedColumns: readonly string[],
|
||
): void => {
|
||
const missing = expectedColumns.filter((column) => !(column in table))
|
||
if (missing.length > 0) {
|
||
throw new Error(
|
||
`Tabela drizzle "${tableName}" não tem as colunas esperadas: ${missing.join(', ')}. ` +
|
||
'O bulk insert usaria nomes de coluna divergentes do schema atual.',
|
||
)
|
||
}
|
||
}
|
||
|
||
/** Normalizes a drizzle `execute()` result (bare array vs `{ rows }`) into row objects. */
|
||
export const drizzleResultRows = (result: unknown): Array<Record<string, unknown>> => {
|
||
if (Array.isArray(result)) return result as Array<Record<string, unknown>>
|
||
if (
|
||
typeof result === 'object' &&
|
||
result !== null &&
|
||
'rows' in result &&
|
||
Array.isArray((result as { rows: unknown }).rows)
|
||
) {
|
||
return (result as { rows: Array<Record<string, unknown>> }).rows
|
||
}
|
||
return []
|
||
}
|