diff --git a/.cta.json b/.cta.json index 8a495a3..fbb2a92 100644 --- a/.cta.json +++ b/.cta.json @@ -12,9 +12,5 @@ "routerOnly": false, "version": 1, "framework": "react", - "chosenAddOns": [ - "eslint", - "nitro", - "tanstack-query" - ] -} \ No newline at end of file + "chosenAddOns": ["eslint", "nitro", "tanstack-query"] +} diff --git a/.env.example b/.env.example index 4c32ad6..6f3d087 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,4 @@ -DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss \ No newline at end of file +DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss + +# Integrációs tesztek adminisztrátori kapcsolata (adatbázis-létrehozáshoz) +TEST_DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss diff --git a/.gitignore b/.gitignore index 8b25bb5..a5c6d41 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ dist dist-ssr *.local .env +.env.* +oob .nitro .tanstack .wrangler diff --git a/.prettierignore b/.prettierignore index 5322d7f..e8770fc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,9 @@ package-lock.json pnpm-lock.yaml -yarn.lock \ No newline at end of file +yarn.lock + +dist +.output +.nitro +.tanstack +node_modules \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ecfbc0e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +RUN corepack enable \ + && corepack prepare pnpm@11.15.1 --activate + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile + +COPY . . +RUN pnpm build + + +FROM node:22-alpine AS runtime + +WORKDIR /app + +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=3000 + +COPY --from=build --chown=node:node /app/.output ./.output + +USER node + +EXPOSE 3000 + +CMD ["node", ".output/server/index.mjs"] \ No newline at end of file diff --git a/README.md b/README.md index 7c9bdd9..2628fac 100644 --- a/README.md +++ b/README.md @@ -1,218 +1,167 @@ -Welcome to your new TanStack Start app! +# BSS weboldal V0 -# Getting Started +A Budavári Schönherz Studió videóarchívum és szerkesztői rendszerének lokálisan +futtatható V0 megvalósítása (TanStack Start + PostgreSQL + Authentik). -To run this application: +## Dokumentáció -```bash -pnpm install -pnpm dev -``` - -# Building For Production +- [V0 követelményspecifikáció](./docs/product-specification.md) +- [Kártyákra bontott implementációs terv](./docs/implementation-plan.md) +- [Implementációs állapot](./docs/implementation-progress.md) +- [OOB bemenetek](./docs/oob-inputs.md) +- [Példa OOB config](./docs/examples/oob-config.example.json) +- [Példa seed JSON](./docs/examples/seed.example.json) -To build this application for production: +## Előfeltételek -```bash -pnpm build -``` +- Node 24+ és pnpm (`corepack enable`) +- Docker (PostgreSQL + Authentik) -## Testing +## Telepítés és indítás (tiszta klónból) -This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with: +A sorrend a specifikáció 17.2 fejezetét követi. Adatbázis-kézi módosítás +nincs szükség egyik lépéshez sem. ```bash -pnpm test -``` - -## Styling - -This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. - -### Removing Tailwind CSS - -If you prefer not to use Tailwind CSS: - -1. Remove the demo pages in `src/routes/demo/` -2. Replace the Tailwind import in `src/styles.css` with your own styles -3. Remove `tailwindcss()` from the plugins array in `vite.config.ts` -4. Uninstall the packages: `pnpm add @tailwindcss/vite tailwindcss --dev` - -## Linting & Formatting - - -This project uses [eslint](https://eslint.org/) and [prettier](https://prettier.io/) for linting and formatting. Eslint is configured using [tanstack/eslint-config](https://tanstack.com/config/latest/docs/eslint). The following scripts are available: - -```bash -pnpm lint -pnpm format -pnpm check -``` - - -## Deploy with Nitro - -This project uses Nitro as a generic server adapter, so it can run on any Node-compatible host. - -```bash -npm run build -node dist/server/index.mjs -``` - -The build output is a self-contained Node server. To deploy, push the `dist/` directory to your host (Render, Fly.io, your own VPS, etc.) and run the server command above. - -For host-specific presets (Vercel, Netlify, Cloudflare, AWS Lambda, etc.) and tuning, see https://v3.nitro.build/deploy. - - - -## Routing - -This project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`. - -### Adding A Route - -To add a new route to your application just add a new file in the `./src/routes` directory. - -TanStack will automatically generate the content of the route file for you. - -Now that you have two routes you can use a `Link` component to navigate between them. - -### Adding Links - -To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. - -```tsx -import { Link } from "@tanstack/react-router"; -``` - -Then anywhere in your JSX you can use it like so: - -```tsx -About -``` - -This will create a link that will navigate to the `/about` route. - -More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). - -### Using A Layout - -In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you render `{children}` in the `shellComponent`. - -Here is an example layout that includes a header: - -```tsx -import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' - -export const Route = createRootRoute({ - head: () => ({ - meta: [ - { charSet: 'utf-8' }, - { name: 'viewport', content: 'width=device-width, initial-scale=1' }, - { title: 'My App' }, - ], - }), - shellComponent: ({ children }) => ( - - - - - -
- -
- {children} - - - - ), -}) -``` - -More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). - -## Server Functions - -TanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components. - -```tsx -import { createServerFn } from '@tanstack/react-start' +# 1. Függőségek telepítése +pnpm install -const getServerTime = createServerFn({ - method: 'GET', -}).handler(async () => { - return new Date().toISOString() -}) +# 2. Helyi titkok és Authentik blueprint generálása (idempotens) +pnpm infra:bootstrap -// Use in a component -function MyComponent() { - const [time, setTime] = useState('') - - useEffect(() => { - getServerTime().then(setTime) - }, []) - - return
Server time: {time}
-} -``` +# 3. PostgreSQL és Authentik indítása +docker compose -f docker-compose.dev.yml up -d +export DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss -## API Routes +# 4. Migrációk futtatása tiszta adatbázison +pnpm db:migrate -You can create API routes by using the `server` property in your route definitions: +# 5. OOB fájlok ellenőrzése (konkrét magyar hibaüzenet hiány esetén) +pnpm check:oob -```tsx -import { createFileRoute } from '@tanstack/react-router' -import { json } from '@tanstack/react-start' +# 6. Seed betöltése (opcionális; az oob/seed.json-t a scraper állítja elő, +# lásd docs/oob-inputs.md). Idempotens: újrafuttatás nem duplikál. +pnpm db:seed -export const Route = createFileRoute('/api/hello')({ - server: { - handlers: { - GET: () => json({ message: 'Hello, World!' }), - }, - }, -}) -``` +# 7. Alkalmazás indítása — induláskor lefut az első tagcache-szinkron is +pnpm dev # http://localhost:3000 -## Data Fetching - -There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. - -For example: - -```tsx -import { createFileRoute } from '@tanstack/react-router' - -export const Route = createFileRoute('/people')({ - loader: async () => { - const response = await fetch('https://swapi.dev/api/people') - return response.json() - }, - component: PeopleComponent, -}) - -function PeopleComponent() { - const data = Route.useLoaderData() - return ( - - ) -} +# 8. Minőségi kapu +pnpm typecheck && pnpm lint && pnpm check +TEST_DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss pnpm test +pnpm build ``` -Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters). - -# Demo files - -Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. - -# Learn More - -You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). - -For TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start). +Megjegyzések: + +- A `pnpm infra:bootstrap` meglévő titkokat nem ír felül; újraindítás nem + duplikál. +- A seed stáblistája a bootstrap tesztprofiljaihoz kötődik (`tag-dev`, + `vezetoseg-dev`, …); ha a tagcache még üres, az importer magyar hibaüzenettel + kéri a szinkron lefuttatását (alkalmazásindítás vagy kézi szinkron az + `/admin/members` oldalon). +- Az Authentik felülete: http://127.0.0.1:9000 + +## Tesztfelhasználók + +Jelszavak: `oob/local-secrets.json` (gitignore-olt). + +| Felhasználó | Szerep | +| --------------------- | --------------------------------------------------------------------- | +| (nincs bejelentkezés) | névtelen látogató — csak publikus videók | +| schonherz-dev | bejelentkezett schönherzes — publikus + schönherz videók, admin nincs | +| tag-dev | BSS-tag — minden láthatóság, adminjogok | +| vezetoseg-dev | vezetőség — kibővített adminjogok | +| további `-dev` userek | szintetikus tagprofilok minden tagsági státuszhoz | + +## Demo forgatókönyvek szereplőnként + +### Névtelen látogató + +1. Nyisd meg http://localhost:3000 — homepage normál vagy kiemelt/live hero-val. +2. `/videos`: csak publikus videók jelennek meg; korlátozott tartalom metaadata + sem szivárog (keresésben, eseményoldalakon és kapcsolódó videóknál sem). +3. Nyiss meg egy videót: player, kapcsolódó videók, címkére kattintva aktív + szűrős videólista. +4. A navbar Belépés gombja belépésre visz, megtartva a kért oldalt. + +### Schönherzes (schonherz-dev) + +1. Jelentkezz be; a `/videos` listában már a schönherz láthatóságú videók is + megjelennek. +2. `/admin` közvetlenül: magyar tiltóoldal (403), sidebar sem látszik. + +### BSS-tag (tag-dev) + +1. Jelentkezz be, menj `/admin`-ra: Videók lista nyílik. +2. Hozz létre piszkozatot (Új videó), adj meg érvénytelen média-URL-t — + piszkozatként menthető; a Publikálás magyar hibával elutasítja. +3. Érvényes URL-lel (`https://v.bsstudio.hu/…`) publikálás — a média + hálózati ellenőrzése mockolható fejlesztésben, élesben HEAD-kéréssel fut. +4. Egynapos eseményhez rendelve a videó automatikusan dátumot kap; többnaposnál + a hiányzó dátum publikálást blokkol, a tartományon kívüli figyelmeztet. +5. Címkék és stáblista kezelése; archiválás és lomtárba helyezés. + +### Vezetőség (vezetoseg-dev) + +1. Lomtár (`/admin/trash`): visszaállítás archivált állapotba, kapcsolatokkal. +2. Live és kiemelés (`/admin/homepage`): YouTube URL ütemezése (átfedés + kliensen és szerveren is tiltott), Indítás most / Lezárás most, kiemelt + videó választása, Rólunk-videók rendezése. +3. Katalógusok: címke létrehozás/összevonás/törlés (használt címke csak név + beírásával), stábszerepek sorrendezése. +4. Tagok (`/admin/members`): szinkronállapot, kézi szinkron (csak olvasható + profilok). +5. Auditnapló (`/admin/audit`): minden előző lépés előtte-utána értékkel. +6. Homepage: a kiemelt videó hero-ként jelenik meg; live aktiválásakor a + prioritás frissítés nélkül vált (percenkénti ellenőrzés). +7. 30 napos törlés szimulációja: lomtárazz egy videót, majd az idő + előretolásával (tesztóra) a napi feladat véglegesen törli — külső médiafájl + érintetlen marad. + +## Ismert V0-korlátok + +- Éles telepítés nem része a V0-nak; a célkörnyezet a dokumentált lokális futtatás. +- A külső MP4 URL-ek publikusak; a média tényleges hozzáférés-védelme külön feladat. +- Email és mobil mezők nem léteznek; a tagprofilok csak olvashatók (Authentik forrás). +- Audit export/visszaállítás és tömeges adminműveletek szándékosan nincsenek. +- Rating, komment, share/download gomb, tanfolyamkezelés nem része a V0-nak. +- A régi Drupal linkek (/video, /event, /user) átirányítása production feladat. +- A kereső popoverje csoportonként öt találtat mutat thumbnail nélkül. + +## Production előtti backlog (nem blokkolja a V0-t) + +Teljes régioldal-migráció; Drupal linkátirányítások; production telepítési +pipeline; UptimeRobot; backup/visszaállítás; védett média; IP-alapú egyedi +nézettség; tanfolyam- és felkéréskezelő integráció; tömeges adminműveletek; +formális akadálymentességi audit. + +## Fejlesztői parancsok + +| Parancs | Funkció | +| -------------------------------------- | ------------------------------------ | +| `pnpm dev` | fejlesztői szerver | +| `pnpm check` | formátumellenőrzés (Prettier) | +| `pnpm lint` | ESLint | +| `pnpm typecheck` | route-generálás + `tsc --noEmit` | +| `TEST_DATABASE_URL=… pnpm test` | unit + integrációs tesztek | +| `pnpm build` | produkciós build (Nitro) | +| `pnpm db:generate` / `pnpm db:migrate` | migráció generálása / futtatása | +| `pnpm db:seed` | idempotens seed import | +| `pnpm check:oob` | OOB config validáció | +| `pnpm infra:bootstrap` | lokális titkok + Authentik blueprint | + +## Tiszta klónból végzett átadási próba + +Az elfogadási bemutató (spec 20/17) a fenti „Telepítés és indítás” szakasz +lépéseit követi adatbázis-kézi módosítás nélkül, majd a négy szereplő +demoforgatókönyvét futtatja végig. Ellenőrzési pontok: + +- `pnpm check:oob` hiányzó OOB elemnél konkrét magyar hibát ad, az alkalmazás + nem indul félkonfiguráltan; +- `pnpm db:migrate` tiszta adatbázison hibátlanul lefut; +- `pnpm db:seed` újrafuttatva nem duplikál; +- a tesztek determinisztikus mockokkal futnak (külső média, YouTube, + Authentik hívások élő elérése nélkül); +- a kizárt V0 elemek (rating, komment, médiafeltöltés, tömeges műveletek stb.) + sehol nem jelennek meg. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 883c527..8cba9f7 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -4,14 +4,21 @@ services: image: postgres:18.4 restart: always ports: - - "5582:5432" + - '5582:5432' environment: POSTGRES_USER: bss POSTGRES_PASSWORD: bss POSTGRES_DB: bss volumes: - bss-dev_db:/var/lib/postgresql -# - ./assets/initdb.sql:/docker-entrypoint-initdb.d/initdb.sql + healthcheck: + test: + - CMD-SHELL + - pg_isready -U bss -d bss + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s networks: - bss-dev @@ -21,7 +28,7 @@ services: image: dpage/pgadmin4:9.16 restart: always ports: - - "8081:80" + - '8081:80' environment: PGADMIN_DEFAULT_EMAIL: bss@bss.dev PGADMIN_DEFAULT_USERNAME: bss @@ -31,7 +38,6 @@ services: - bss-dev_pgadmin:/var/lib/pgadmin networks: - bss-dev - - bss-dev-authentik bss-dev-authentik-postgresql: container_name: bss-dev-authentik-postgresql @@ -61,21 +67,21 @@ services: depends_on: bss-dev-authentik-postgresql: condition: service_healthy + env_file: + - path: ./oob/authentik.env + required: false environment: AUTHENTIK_POSTGRESQL__HOST: bss-dev-authentik-postgresql AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__PASSWORD: authentik AUTHENTIK_POSTGRESQL__USER: authentik - AUTHENTIK_SECRET_KEY: secret image: authentik/server:2026.5.4 ports: - - "9000:9000" - - "9443:9443" + - '9000:9000' restart: unless-stopped shm_size: 512mb volumes: - - ./assets/data:/data - - ./assets/custom-templates:/templates + - ./oob/authentik/blueprints:/blueprints networks: - bss-dev-authentik @@ -85,30 +91,29 @@ services: depends_on: bss-dev-authentik-postgresql: condition: service_healthy + env_file: + - path: ./oob/authentik.env + required: false environment: AUTHENTIK_POSTGRESQL__HOST: bss-dev-authentik-postgresql AUTHENTIK_POSTGRESQL__NAME: authentik AUTHENTIK_POSTGRESQL__PASSWORD: authentik AUTHENTIK_POSTGRESQL__USER: authentik - AUTHENTIK_SECRET_KEY: secret image: authentik/server:2026.5.4 restart: unless-stopped shm_size: 512mb user: root volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./assets/data:/data - - ./assets/certs:/certs - - ./assets/custom-templates:/templates + - ./oob/authentik/blueprints:/blueprints networks: - bss-dev-authentik networks: - bss-dev: - bss-dev-authentik: + bss-dev: + bss-dev-authentik: volumes: bss-dev_db: bss-dev_pgadmin: bss-dev-authentik_database: - driver: local \ No newline at end of file + driver: local diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..94244bc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,120 @@ +x-authentik-environment: &authentik-environment + AUTHENTIK_POSTGRESQL__HOST: authentik-db + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_DB_PASSWORD} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + +services: + app: + build: + context: . + target: runtime + restart: unless-stopped + container_name: bss-app + environment: + NODE_ENV: production + HOST: 0.0.0.0 + PORT: 3000 + DATABASE_URL: postgresql://bss:${BSS_DB_PASSWORD}@app-db:5432/bss + BSS_OOB_CONFIG: /app/oob/config.json + depends_on: + app-db: + condition: service_healthy + ports: + - "8070:3000" + volumes: + - ./oob:/app/oob:ro + + app-db: + image: postgres:18.4-alpine + restart: unless-stopped + container_name: bss-db + environment: + POSTGRES_DB: bss + POSTGRES_USER: bss + POSTGRES_PASSWORD: ${BSS_DB_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U bss -d bss"] + interval: 10s + timeout: 5s + retries: 10 + volumes: + - app_database:/var/lib/postgresql + + # Run manually with: + # docker compose --profile tools run --rm db-push + db-migrate: + build: + context: . + target: build + profiles: ["tools"] + command: ["pnpm", "db:migrate"] + environment: + DATABASE_URL: postgresql://bss:${BSS_DB_PASSWORD}@app-db:5432/bss + depends_on: + app-db: + condition: service_healthy + + volumes: + - ./oob:/app/oob + + authentik-db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: authentik + POSTGRES_USER: authentik + POSTGRES_PASSWORD: ${AUTHENTIK_DB_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U authentik -d authentik"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 20s + volumes: + - authentik_database:/var/lib/postgresql/data + + authentik-server: + image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2026.5.4} + command: server + restart: unless-stopped + shm_size: 512mb + container_name: bss-authentik + environment: + <<: *authentik-environment + depends_on: + authentik-db: + condition: service_healthy + ports: + - "8071:9000" + - "8072:9443" + volumes: + - authentik_data:/data + - authentik_templates:/templates + + authentik-worker: + image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2026.5.4} + command: worker + restart: unless-stopped + user: root + shm_size: 512mb + environment: + <<: *authentik-environment + depends_on: + authentik-db: + condition: service_healthy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - authentik_data:/data + - authentik_certs:/certs + - authentik_templates:/templates + - ./oob/authentik/blueprints:/blueprints/custom + + +volumes: + app_database: + authentik_database: + authentik_data: + authentik_certs: + authentik_templates: \ No newline at end of file diff --git a/docs/examples/oob-config.example.json b/docs/examples/oob-config.example.json new file mode 100644 index 0000000..9d03ab2 --- /dev/null +++ b/docs/examples/oob-config.example.json @@ -0,0 +1,56 @@ +{ + "authentik": { + "issuerUrl": "https://TÖLTSD-KI/authentik/application/o/bss-stack/", + "clientId": "TÖLTSD-KI", + "clientSecret": "TÖLTSD-KI-titok-nem-kerülhet-gitbe", + "scopes": ["openid", "profile", "email"], + "claims": { + "sub": "sub", + "username": "preferred_username", + "fullName": "name", + "nickname": "nickname", + "avatarUrl": "picture" + }, + "groups": { + "schonherz": "TÖLTSD-KI-schönherzes-csoport-neve", + "tag": "TÖLTSD-KI-tag-csoport-neve", + "vezetoseg": "TÖLTSD-KI-vezetőség-csoport-neve" + }, + "attributes": { + "membershipStatus": { + "attribute": "TÖLTSD-KI-tagsági-státusz-attribútum-kulcsa", + "values": { + "TÖLTSD-KI-nyers-érték": "studio_member", + "TÖLTSD-KI-nyers-érték-2": "senior_active" + } + }, + "joinedSemester": { + "attribute": "TÖLTSD-KI-csatlakozási-félév-attribútum-kulcsa", + "rules": [ + { + "pattern": "^(\\d{4})\\s+(ősz|őszi)$", + "semester": "autumn" + }, + { + "pattern": "^(\\d{4})\\s+(tavasz|tavaszi)$", + "semester": "spring" + } + ] + }, + "introduction": "TÖLTSD-KI-bemutatkozás-attribútum-kulcsa" + }, + "sync": { + "username": "svc-bss-sync", + "token": "" + } + }, + "media": { + "allowedHosts": ["v.bsstudio.hu"] + }, + "youtube": { + "oEmbedEndpoint": "https://www.youtube.com/oembed" + }, + "seed": { + "path": "oob/seed.json" + } +} diff --git a/docs/examples/seed.example.json b/docs/examples/seed.example.json new file mode 100644 index 0000000..e82c435 --- /dev/null +++ b/docs/examples/seed.example.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "events": [ + { + "key": "gala-2025", + "title": "Tavaszi Gála 2025", + "description": "A stúdió tavaszi gálaműsora.", + "thumbnailUrl": null, + "startDate": "2025-05-10", + "endDate": null, + "status": "published" + } + ], + "tags": ["Gála", "Adás"], + "staffRoles": ["Operatőr", "Vágó"], + "videos": [ + { + "key": "vid-001", + "title": "Gálanyitó 2025", + "description": "A gála felvétele.", + "guests": null, + "songs": "Előadó - Szám címe", + "videoUrl": "https://v.bsstudio.hu/seed/galanyito-2025.mp4", + "thumbnailUrl": "https://v.bsstudio.hu/seed/galanyito-2025.jpg", + "visibility": "public", + "status": "published", + "recordedAt": "2025-05-10", + "publishedAt": "2025-06-01T12:00:00Z", + "eventKey": "gala-2025", + "tags": ["Gála", "Adás"], + "staff": [ + { "username": "tag-dev", "role": "Operatőr" }, + { "username": "vezetoseg-dev", "role": "Vágó" } + ] + } + ] +} diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md new file mode 100644 index 0000000..87d1996 --- /dev/null +++ b/docs/implementation-plan.md @@ -0,0 +1,978 @@ +# BSS weboldal V0 kártyákra bontott implementációs terve + +## 1. A terv használata + +Ez a terv a [követelményspecifikációra](./product-specification.md) épül. A kártyák függőségi sorrendben követik egymást. Egy hullámon belül a független kártyák párhuzamosan végezhetők. + +Relatív méretek: + +- `S`: kis, egy jól körülhatárolt változtatás; +- `M`: közepes, több réteget vagy több tesztesetet érint; +- `L`: nagy, több PR-re bontható, de egy közös elfogadási eredménye van. + +Minden kártya V0-prioritású. A sorrendet a függőségek határozzák meg, nem a sorszám önmagában. + +## 2. Közös elkészültségi feltétel + +Egy kártya akkor kész, ha: + +- a kód és az adatbázisváltozás verziókezelt; +- a jogosultságot a szerver ellenőrzi; +- a sikeres, üres, betöltési és hibás állapot kezelve van; +- a releváns unit vagy integrációs teszt elkészült; +- a typecheck, lint és érintett tesztek zöldek; +- az új config és lokális lépés dokumentált; +- a mobil- és asztali felület használható; +- nincs néma adatvesztés vagy utolsó mentés nyer viselkedés. + +## 3. Végrehajtási hullámok + +| Hullám | Cél | Kártyák | +| ------ | ----------------------------------------- | ----------------- | +| 0 | Stabil alap és tiszta séma | BSS-001 - BSS-005 | +| 1 | Auth, jogosultság és közös infrastruktúra | BSS-006 - BSS-011 | +| 2 | Tartalmi domainek | BSS-012 - BSS-018 | +| 3 | Publikus felület | BSS-019 - BSS-026 | +| 4 | Adminfelület | BSS-027 - BSS-033 | +| 5 | Seed, SEO, minőség és átadás | BSS-034 - BSS-038 | + +A fő kritikus út: + +`BSS-001 -> BSS-004 -> BSS-005 -> BSS-006 -> BSS-007 -> BSS-008 -> BSS-010 -> BSS-014 -> BSS-028 -> BSS-037 -> BSS-038` + +### Lefedettségi térkép + +| Specifikációs terület | Elsődleges kártyák | +| ------------------------------------- | -------------------------------------------- | +| Lokális alap, config és séma | BSS-001 - BSS-005 | +| Authentik, session és jogosultság | BSS-006 - BSS-008 | +| Slug, audit, háttérfeladat és média | BSS-009 - BSS-011 | +| Címkék és stábszerepek | BSS-012, BSS-030 | +| Események | BSS-013, BSS-022, BSS-029 | +| Videók és megtekintés | BSS-014 - BSS-016, BSS-020, BSS-021, BSS-028 | +| Homepage, live és Rólunk | BSS-017, BSS-024, BSS-026, BSS-031 | +| Keresés | BSS-018, BSS-020, BSS-025 | +| Tagok | BSS-008, BSS-023, BSS-032 | +| Lomtár és audit UI | BSS-033 | +| Seed, SEO, felületi minőség és átadás | BSS-034 - BSS-038 | + +## 4. Kártyák + +### BSS-001 - Toolchain stabilizálása + +- Méret: `M` +- Függőség: nincs + +Cél: reprodukálható, zöld fejlesztői alap létrehozása a jelenlegi nightly és `latest` függőségek helyett. + +Tartalom: + +- kompatibilis verziók rögzítése; +- a Nitro és Vite tesztindítási hiba javítása; +- a jelenlegi TypeScript- és lint-hibák javítása; +- a generált route-kezelés tisztázása; +- egységes `typecheck`, `lint`, `test`, `build` parancsok. + +Elfogadási feltételek: + +- tiszta telepítés után ugyanazok a verziók kerülnek fel; +- `pnpm lint`, typecheck, `pnpm test` és build sikeres; +- nincs nightly vagy indokolatlan `latest` production-függőség; +- a változtatás nem módosít termékfunkciót. + +### BSS-002 - Tesztalap és vezérelhető idő + +- Méret: `M` +- Függőség: BSS-001 + +Cél: olyan tesztkörnyezet kialakítása, amelyben a jogosultság, külső szolgáltatások és időzített törlés megbízhatóan vizsgálható. + +Tartalom: + +- unit és integrációs teszt setup; +- külön tesztadatbázis-kezelés; +- vezérelhető óra a 30 napos törléshez és live-váltáshoz; +- HTTP mock a `v.bsstudio.hu`, YouTube oEmbed és Authentik hívásokhoz; +- közös fixture factoryk. + +Elfogadási feltételek: + +- teszt nem használ élő külső szolgáltatást; +- a tesztóra tetszőleges időpontra állítható; +- egy integrációs smoke test valódi PostgreSQL ellen lefut; +- a tesztek egymástól függetlenül ismételhetők. + +### BSS-003 - OOB konfigurációs szerződés + +- Méret: `M` +- Függőség: BSS-001 + +Cél: a hiányzó Authentik mapping, titkok és seed fájlok formájának rögzítése. + +Tartalom: + +- típusos config séma; +- Authentik claim-, attribútum- és csoportmapping; +- csatlakozási félév és tagsági státusz transzformációs szabályai; +- médiahost- és YouTube-konfiguráció; +- OOB seed fájl helye; +- induláskori validáció és konkrét hibaüzenetek. + +Elfogadási feltételek: + +- hiányzó kötelező config mellett az alkalmazás nem indul félkonfigurált auth módban; +- ismeretlen Authentik státusz vagy félév nem kap kitalált értéket; +- titok nem kerül kliensbundle-be vagy gitbe; +- lokális tesztconfig külön használható. + +### BSS-004 - Új adatbázisséma és migrációs alap + +- Méret: `L` +- Függőség: BSS-001 + +Cél: a prototípus követelményekkel ütköző sémájának lecserélése. + +Tartalom: + +- videók, események, címkék, stábszerepek és stábkapcsolatok; +- videónként legfeljebb egy opcionális esemény; +- tagcache és Authentik `sub` kulcs; +- csatlakozási félév nyers és normalizált értékei; +- slug és slugtörténet; +- tartalomállapotok és videólomtár; +- manuális kapcsolódóvideó-sorrend; +- live, kiemelés és Rólunk-videólista; +- auditnapló; +- megtekintésszám és anonim session tárolás; +- szükséges indexek, egyediségek és idegen kulcsok. + +Elfogadási feltételek: + +- tiszta PostgreSQL adatbázison a migráció lefut; +- ugyanaz a videó nem kapcsolható két eseményhez; +- jogosultsági és stábszerep nem ugyanaz a tábla; +- profilkép bináris tárolása megszűnik; +- timestamp és naptári dátum mezők nem keverednek; +- a séma adatbázis-korlátokkal is védi a legfontosabb invariánsokat. + +### BSS-005 - Lokális infrastruktúra és Authentik bootstrap + +- Méret: `L` +- Függőség: BSS-003, BSS-004 + +Cél: dokumentáltan felépíthető PostgreSQL és Authentik tesztkörnyezet. + +Tartalom: + +- compose konfiguráció rendezése; +- Authentik blueprint vagy bootstrap script; +- schönherzes, tag és vezetőségi tesztesetek; +- szintetikus tagprofilok és csoportok; +- helyi titkok biztonságos előállítása; +- adatbázis-migrációs parancs. + +Elfogadási feltételek: + +- új fejlesztő dokumentált lépésekkel felépíti a környezetet; +- mindhárom nézői szint és mindkét adminjog tesztelhető; +- tesztjelszó nem kerül gitbe; +- újraindítás nem duplikálja a bootstrap adatokat. + +### BSS-006 - OIDC belépés és session + +- Méret: `L` +- Függőség: BSS-003, BSS-005 + +Cél: Authentik OIDC Authorization Code flow PKCE-vel és szerveroldali sessionnel. + +Tartalom: + +- login, callback és logout útvonal; +- HTTP-only, `SameSite=Lax` cookie; +- access token szerveroldali kezelése; +- visszatérési URL megőrzése; +- legfeljebb órás szerepfrissítés; +- Authentik-kiesési viselkedés. + +Elfogadási feltételek: + +- token nem kerül `localStorage`-ba; +- névtelen felhasználó belépés után visszajut az eredeti oldalra; +- Authentik-kieséskor új login nem sikerül, a publikus oldal működik; +- lejárt sessionnel mutáció nem hajtható végre. + +### BSS-007 - Jogosultsági policy és szerveroldali guardok + +- Méret: `M` +- Függőség: BSS-006 + +Cél: egyetlen, tesztelt helyen érvényesíteni a nézői és adminjogokat. + +Tartalom: + +- névtelen, schönherzes, tag és vezetőségi policy; +- videóláthatóság SQL-feltételei; +- adminműveletek jogosultsági mátrixa; +- `403`, `404` és loginra irányítás szabályai; +- kliensoldali navigáció szűrése a szerveroldali guard mellett. + +Elfogadási feltételek: + +- tiltott videó metaadata nem kerül a válaszba; +- tag nem kezelhet címkekatalógust, live-ot vagy auditot; +- vezetőségi jog magában foglalja a tagjogot; +- minden policy-ág integrációs tesztet kap. + +### BSS-008 - Authentik tagcache és szinkron + +- Méret: `L` +- Függőség: BSS-002, BSS-003, BSS-004, BSS-006, BSS-007 + +Cél: publikus tagadatok kiszolgálása helyi, csak olvasható cache-ből. + +Tartalom: + +- induláskori, óránkénti és kézi szinkron; +- mapping és normalizálás; +- nyers csatlakozási félév megőrzése; +- ismeretlen státusz és formátum hibakezelése; +- eltűnt tag utolsó ismert rekordjának megtartása; +- szinkronállapot és hibák; +- szerepváltozás frissítése. + +Elfogadási feltételek: + +- publikus kérés nem hívja az Authentiket; +- eltűnt tag stáblistája nem szakad el; +- hibás profil nem kerül publikus csoportba; +- változatlan szinkron nem ír auditbejegyzést; +- kézi szinkront csak vezetőség indíthat. + +### BSS-009 - Közös slug, audit és optimista zárolás + +- Méret: `L` +- Függőség: BSS-004, BSS-007 + +Cél: közös infrastruktúra minden szerkeszthető entitáshoz. + +Tartalom: + +- slugképzés, ütközéskezelés és átirányítási előzmény; +- `createdBy`, `updatedBy`, verzió és timestamp kezelés; +- elavult mentés blokkolása; +- tranzakciós audit előtte-utána értékekkel; +- `system` szereplő; +- plain text és hosszvalidációk. + +Elfogadási feltételek: + +- régi slug az újra irányít; +- két párhuzamos mentés közül az elavult kérés konfliktust kap; +- sikertelen tranzakció nem hagy auditot vagy félkész adatot; +- audit nem módosítható az alkalmazásból. + +### BSS-010 - Háttérfeladat-futtató, health és adminriasztás + +- Méret: `M` +- Függőség: BSS-002, BSS-004, BSS-008, BSS-009 + +Cél: biztonságos időzített feladatok külső worker nélkül. + +Tartalom: + +- PostgreSQL advisory lock; +- induláskori és óránkénti sync ütemezés; +- regisztrálható napi lomtártörlési és live időzítő feladatok; +- `/health/live` és `/health/ready`; +- tartós vezetőségi hibasáv és részletes hibanapló. + +Elfogadási feltételek: + +- két alkalmazáspéldány nem futtatja ugyanazt a feladatot kétszer; +- health válasz nem szivárogtat titkot; +- a tesztóra vezérli a regisztrált feladatokat; +- háttérhiba nem állítja le a publikus cache-t. + +### BSS-011 - Média- és YouTube-validátor + +- Méret: `M` +- Függőség: BSS-002, BSS-003 + +Cél: csak valóban használható média kerüljön publikált tartalomba. + +Tartalom: + +- `v.bsstudio.hu` hostellenőrzés; +- `HEAD`, timeout, redirect és content type ellenőrzés; +- Range GET tartalék `405` és `501` esetére; +- YouTube URL-normalizálás; +- oEmbed ellenőrzés; +- piszkozat és publikálás eltérő hibakezelése. + +Elfogadási feltételek: + +- a bsstudio.hu főoldalára irányító hiányzó média nem fogadható el; +- MP4 helyén kép és thumbnail helyén HTML nem publikálható; +- hibás URL piszkozatban menthető; +- teszt nem tölt le teljes médiafájlt. + +### BSS-012 - Címke- és stábszerep-domain + +- Méret: `M` +- Függőség: BSS-004, BSS-008, BSS-009 + +Cél: a két katalógus szabályainak szerveroldali megvalósítása. + +Tartalom: + +- címke létrehozás, átnevezés, összevonás és törlés; +- használt címke kapcsolatainak tranzakciós bontása; +- kisbetű- és whitespace-normalizálás; +- ékezeti hasonlóságra figyelmeztetés; +- stábszerep létrehozás, átnevezés, sorrendezés és összevonás; +- használatban lévő stábszerep törlésének tiltása. + +Elfogadási feltételek: + +- tag csak hozzárendelni tud meglévő címkét; +- használt címke törléséhez vezetőségi jog és címbeírás kell; +- szerepösszevonás nem veszít stábkapcsolatot; +- egy videón egy tag több szerepet is kaphat. + +### BSS-013 - Esemény-domain és végleges törlés + +- Méret: `L` +- Függőség: BSS-004, BSS-007, BSS-009, BSS-011 + +Cél: esemény CRUD, állapotkezelés és biztonságos végleges törlés. + +Tartalom: + +- piszkozat, publikált és archivált állapot; +- dátum- és thumbnail-validáció; +- jövőbeli esemény támogatása; +- publikálási feltételek; +- vezetőségi végleges törlés címbeírással; +- videókapcsolatok tranzakciós bontása; +- lista- és részletlekérdezések alapja. + +Elfogadási feltételek: + +- befejezés nem előzheti meg a kezdést; +- tag archiválhat, de nem törölhet végleg; +- eseménytörlés megtartja a videók `recordedAt` értékét; +- esemény és kapcsolatok félállapot nélkül törlődnek. + +### BSS-014 - Videó-domain és életciklus + +- Méret: `L` +- Függőség: BSS-004, BSS-007, BSS-009, BSS-010, BSS-011, BSS-012, BSS-013 + +Cél: a videó minden mezőjének, állapotának és publikálási szabályának szerveroldali megvalósítása. + +Tartalom: + +- piszkozat mentése és azonnali publikálás; +- kötelező thumbnail és MP4; +- alapértelmezett publikus láthatóság; +- múltbeli, de nem jövőbeli `publishedAt`; +- esemény- és `recordedAt` szabályok; +- címke- és stáblista-hozzárendelés; +- archiválás és publikálás; +- lomtár és vezetőségi visszaállítás archivált állapotba; +- napi 30 napos végleges törlési feladat regisztrálása; +- eseménydátum-eltérés figyelmeztetése; +- kiemelés és Rólunk-lista érvénytelenítése állapotváltozáskor. + +Elfogadási feltételek: + +- piszkozat csak címmel menthető; +- publikálás minden kötelező mezőt és médiát ellenőriz; +- egy videónak legfeljebb egy eseménye van; +- eseménymódosítás nem írja felül csendben a videódátumot; +- visszaállítás megőrzi a kapcsolatait. + +### BSS-015 - Kapcsolódó videók szolgáltatása + +- Méret: `M` +- Függőség: BSS-012, BSS-013, BSS-014 + +Cél: manuális, eseményes és közöscímkés kapcsolódó videók egységes kiszolgálása. + +Tartalom: + +- sorrendezett manuális lista; +- azonos esemény öt legutóbbi videója; +- esemény nélkül közös címkéken alapuló rangsor; +- önhivatkozás és duplikáció tiltása; +- állapot- és jogosultsági szűrés. + +Elfogadási feltételek: + +- manuális lista teljesen felülírja az automatikusat; +- az aktuális videó nem szerepel; +- korlátozott videó nem szivárog ki jogosulatlan nézőnek; +- egyező pontszámnál stabil a sorrend. + +### BSS-016 - Megtekintésszámláló + +- Méret: `M` +- Függőség: BSS-002, BSS-004, BSS-014 + +Cél: egyszerű, sessionönként egyszer számoló playerindítás rögzítése. + +Tartalom: + +- anonim, böngésző bezárásáig élő session cookie; +- első sikeres `play` esemény feldolgozása; +- idempotens videó-session kapcsolat; +- párhuzamos kérések kezelése; +- adminlekérdezés. + +Elfogadási feltételek: + +- pause és újraindítás nem növel újra; +- másik böngésző-session növelhet; +- IP és felhasználói előzmény nem tárolódik; +- publikus válasz nem tartalmaz megtekintésszámot. + +### BSS-017 - Homepage, kiemelés, live és Rólunk-domain + +- Méret: `L` +- Függőség: BSS-007, BSS-009, BSS-010, BSS-011, BSS-013, BSS-014 + +Cél: a homepage prioritás és vezetőségi beállítások szerveroldali megvalósítása. + +Tartalom: + +- live, kiemelt és normál prioritás; +- publikus kiemelt videó kiválasztása; +- live időablak, átfedés-tiltás és előzmény; +- `Adás hamarosan` 24 órás szabály; +- `Indítás most` és `Lezárás most`; +- aktiváláskori oEmbed ellenőrzés és fallback; +- Rólunk oldal legfeljebb hat rendezett videója; +- öt vagy hat friss videó és hat esemény lekérdezése. + +Elfogadási feltételek: + +- aktív live mindig felülírja a kiemelést; +- átfedő időablak nem menthető; +- nem publikus vagy archivált videó nem marad kiemelve; +- befejezett live csak adminelőzményben marad; +- Rólunk-listából kiesik az érvénytelen videó. + +### BSS-018 - Keresési szolgáltatás + +- Méret: `L` +- Függőség: BSS-004, BSS-007, BSS-008, BSS-012, BSS-013, BSS-014 + +Cél: súlyozott globális keresés és összetett videószűrés PostgreSQL-ben. + +Tartalom: + +- szükséges `pg_trgm` és ékezetfüggetlen keresési támogatás; +- videó, esemény, tag és címke találatok; +- elfogadott súlyozás; +- videó részletes szűrői; +- címkék `ÉS` logikája; +- relevancia és választható rendezések; +- szerveroldali lapozás; +- jogosultsági feltételek a lekérdezésben. + +Elfogadási feltételek: + +- cím- és névegyezés megelőzi a leírástalálatot; +- zeneszöveg nem kereshető; +- ékezet nélküli és kisebb elgépeléses keresés működik; +- jogosulatlan találat és találatszám sem szivárog; +- azonos lekérdezés stabil sorrendet ad. + +### BSS-019 - Alkalmazásváz, publikus route-ok és hibaoldalak + +- Méret: `M` +- Függőség: BSS-006, BSS-007, BSS-009 + +Cél: a végleges route-struktúra és közös oldalállapotok kialakítása. + +Tartalom: + +- elfogadott angol útvonalak; +- slugfeloldás és régi slug redirect; +- közös navbar, footer és login állapot; +- magyar `403`, `404`, betöltési és hibaoldalak; +- mentetlen űrlap kliensoldali megőrzésének alapja. + +Elfogadási feltételek: + +- piszkozat, archív és lomtár publikus route-ja `404`; +- jogosult és jogosulatlan állapot nem keveredik; +- régi slug az új canonical route-ra irányít; +- mobil navigáció használható. + +### BSS-020 - Publikus videólista + +- Méret: `L` +- Függőség: BSS-014, BSS-018, BSS-019 + +Cél: valós adatokkal működő videókatalógus. + +Tartalom: + +- thumbnail és cím kártya; +- három rendezés; +- elfogadott részletes szűrők; +- címkék `ÉS` kapcsolata; +- 10, 25, 50, 100 oldalméret, alapból 50; +- URL-ben megmaradó állapot; +- jogosultság szerinti eredmények. + +Elfogadási feltételek: + +- lapozáskor nincs duplikáció vagy kihagyás; +- hiányzó `recordedAt` időrendi listában hátul van; +- frissítés és megosztott URL megtartja a szűrőket; +- üres találat külön magyar állapotot mutat. + +### BSS-021 - Videórészlet, player és kapcsolódó tartalom + +- Méret: `L` +- Függőség: BSS-014, BSS-015, BSS-016, BSS-019 + +Cél: a teljes videóoldal valós adatokkal. + +Tartalom: + +- elfogadott blokk-sorrend; +- natív MP4 player, poster és `preload="metadata"`; +- `play` esemény számlálása; +- esemény-, címke- és tagprofil-linkek; +- plain text blokkok; +- kapcsolódó videók; +- médiahiba és újrapróbálás. + +Elfogadási feltételek: + +- autoplay és külön download gomb nincs; +- üres opcionális blokkok nem jelennek meg; +- tiltott kapcsolódó videó nem látszik; +- ugyanaz a session csak egyszer növel számlálót. + +### BSS-022 - Publikus eseménylista és részletoldal + +- Méret: `M` +- Függőség: BSS-013, BSS-014, BSS-019 + +Cél: események listázása és részlete jogosultsággal szűrt származtatott adatokkal. + +Tartalom: + +- thumbnail, cím és videószám overlay; +- kezdődátum szerinti rendezés; +- 50-es alaplapozás; +- részletoldal és videólista; +- thumbnail fallback; +- videókból származtatott, titulus nélküli stáblista. + +Elfogadási feltételek: + +- videószám csak a látható videókat számolja; +- a stáblista nem szivárogtat korlátozott videót; +- esemény videó nélkül is publikus marad; +- `recordedAt` szerinti sorrend működik. + +### BSS-023 - Publikus taglista és tagprofil + +- Méret: `L` +- Függőség: BSS-008, BSS-013, BSS-014, BSS-019 + +Cél: Authentik-cache-ből kiszolgált csoportos tagoldalak és tevékenység. + +Tartalom: + +- vezetőség és tagsági csoportok; +- külön archivált és közreműködő aloldal; +- tagkártya; +- profiladatok; +- év és szerep nézet; +- több szerepnél tudatos ismétlés; +- 50-es `Továbbiak betöltése`; +- jogosultsági videószűrés. + +Elfogadási feltételek: + +- vezetőségi tag nem ismétlődik másik csoportban; +- ismeretlen státuszú profil nem publikus; +- email és mobil nem kerül válaszba; +- a nézet URL-ből visszaállítható. + +### BSS-024 - Homepage és YouTube live felület + +- Méret: `L` +- Függőség: BSS-013, BSS-014, BSS-017, BSS-019 + +Cél: a három homepage-prioritás megjelenítése frissítés nélküli váltással. + +Tartalom: + +- live és kiemelt hero öt friss videóval; +- normál állapot hat videóval; +- minden állapot alatt hat esemény; +- `Adás hamarosan` sáv; +- YouTube nocookie embed autoplay nélkül; +- következő váltás kliensoldali időzítése és percenkénti ellenőrzés. + +Elfogadási feltételek: + +- hero videó nem ismétlődik az oldalsó listában; +- live kezdése és vége kézi frissítés nélkül megjelenik; +- hibás aktiválás fallbacket mutat; +- mobilon minden blokk használható. + +### BSS-025 - Globális kereső és találati oldal + +- Méret: `L` +- Függőség: BSS-018, BSS-019, BSS-020, BSS-022, BSS-023 + +Cél: navbar popover és teljes keresőoldal. + +Tartalom: + +- két karakteres minimum és 250 ms késleltetés; +- öt találat típusonként; +- billentyűzetes kezelés; +- Összes, Videók, Események és Tagok fülek; +- típusonként tíz találat az Összes fülön; +- címke átirányítás aktív videószűrőre; +- üres keresési útmutató. + +Elfogadási feltételek: + +- tag és címke megnevezése nem keveredik; +- popover billentyűzettel nyitható és bezárható; +- tiltott videó metaadata nem jelenik meg; +- részletes videókeresés megtartja a queryt. + +### BSS-026 - Rólunk oldal és tanfolyam-átirányítás + +- Méret: `S` +- Függőség: BSS-017, BSS-019, BSS-021 + +Cél: a két egyszerű publikus útvonal véglegesítése. + +Tartalom: + +- verziókezelt Rólunk-szöveg; +- legfeljebb hat rendezett publikus videó; +- `/courses` átirányítás ugyanabban a fülben. + +Elfogadási feltételek: + +- érvénytelen videó nem marad a Rólunk-listában; +- helyi tanfolyaműrlap és ál-sikerüzenet megszűnik; +- az átirányítás szerveroldalon is működik. + +### BSS-027 - Adminváz és sidebar + +- Méret: `M` +- Függőség: BSS-007, BSS-019 + +Cél: YouTube Studio jellegű információs szerkezet BSS-arculattal. + +Tartalom: + +- elfogadott sidebar-elemek; +- tag- és vezetőségi elemek eltérő megjelenése; +- belépés utáni Videók kezdőoldal; +- közös táblázat és mobil kártyanézet; +- sessionlejárati és jogosultsági hibaállapot. + +Elfogadási feltételek: + +- tag nem lát vezetőségi menüpontot; +- közvetlen URL-en a szerver is tilt; +- mobilon nem kell vízszintesen kezelhetetlen táblát görgetni; +- nincs üres dashboard. + +### BSS-028 - Videó-adminlista és szerkesztő + +- Méret: `L` +- Függőség: BSS-014, BSS-015, BSS-027 + +Cél: a teljes videóéletciklus kezelése adatbázis-kézi beavatkozás nélkül. + +Tartalom: + +- elfogadott listaoszlopok és szűrők; +- létrehozás és szerkesztés; +- piszkozat vagy azonnali publikálás; +- médiaellenőrzés visszajelzése; +- címke-, esemény-, stáb- és manuális kapcsolódóvideó-kezelés; +- dátumwarningok; +- archiválás és lomtár; +- elavult mentési konfliktus; +- mentetlen adat megőrzése új belépésnél. + +Elfogadási feltételek: + +- tag nem tud új címkét létrehozni az űrlapon; +- kötelező mező nélkül publikálás nem sikerül; +- médiahiba mellett piszkozat menthető; +- az elavult mentés nem írja felül a frissebb adatot; +- tömeges művelet nincs. + +### BSS-029 - Esemény-adminlista és szerkesztő + +- Méret: `M` +- Függőség: BSS-013, BSS-027 + +Cél: esemény CRUD és vezetőségi végleges törlés. + +Tartalom: + +- elfogadott listaoszlopok és szűrők; +- piszkozat, publikálás és archiválás; +- dátumintervallum és thumbnail; +- videószám; +- címbeírásos végleges törlés vezetőségnek; +- érintett videók leválasztásának összefoglalója. + +Elfogadási feltételek: + +- tag nem kap végleges törlés gombot; +- vezetőség látja, hány videó válik le; +- törlés tranzakciós; +- tömeges törlés nincs. + +### BSS-030 - Címke- és stábszerep-admin + +- Méret: `M` +- Függőség: BSS-012, BSS-027 + +Cél: a két vezetőségi katalógus kezelőfelülete. + +Tartalom: + +- címke létrehozás, átnevezés, összevonás és törlés; +- használati szám és warning; +- címbeírásos használtcímke-törlés; +- stábszerep létrehozás, átnevezés, összevonás és drag vagy gombos sorrendezés; +- használt szerep törlésének blokkolása. + +Elfogadási feltételek: + +- hasonló, ékezetelt címke figyelmeztetést ad; +- kapcsolatbontás és összevonás eredménye előre látható; +- tag közvetlen API-hívással sem módosíthat katalógust; +- `displayOrder` minden publikus stábnézetben érvényes. + +### BSS-031 - Live, kiemelés és Rólunk admin + +- Méret: `L` +- Függőség: BSS-017, BSS-027 + +Cél: a vezetőség minden homepage- és Rólunk-beállítást elvégezhet egy helyen. + +Tartalom: + +- publikus kiemelt videó választása és törlése; +- YouTube URL normalizálás és előnézet; +- live kezdés, befejezés, átfedésjelzés; +- `Indítás most`, `Lezárás most`, másolatként új ütemezés; +- befejezett live előzmény; +- legfeljebb hat Rólunk-videó rendezése; +- aktiválási hibák. + +Elfogadási feltételek: + +- nem publikus videó nem választható kiemelésnek; +- hibás YouTube azonosító nem publikálható; +- átfedés kliensen és szerveren is blokkolt; +- tag nem látja vagy hívhatja a műveleteket. + +### BSS-032 - Rejtett tagdiagnosztika + +- Méret: `M` +- Függőség: BSS-008, BSS-027 + +Cél: a vezetőség lássa az Authentik-cache állapotát helyi profilszerkesztés nélkül. + +Tartalom: + +- profilok és utolsó szinkron; +- hiányzó vagy hibás mappingek; +- Authentikből eltűnt tagok; +- kézi szinkron; +- Authentik adminra mutató link; +- tartós szinkronhiba-sáv. + +Elfogadási feltételek: + +- helyi profil- és jogosultságmódosítás nincs; +- kézi szinkron auditált; +- tag nem látja a menüpontot vagy az adatot; +- nyers hiba nem szivárogtat titkot. + +### BSS-033 - Lomtár és audit admin + +- Méret: `L` +- Függőség: BSS-009, BSS-010, BSS-014, BSS-027 + +Cél: törölt videók és minden adminmódosítás ellenőrizhető kezelése. + +Tartalom: + +- minden tag számára látható videólomtár; +- törlő, időpont és hátralévő idő; +- vezetőségi visszaállítás archivált állapotba; +- napi végleges törlés állapota; +- vezetőségi auditlista; +- szereplő-, művelet-, entitás- és dátumszűrő; +- előtte-utána részletnézet. + +Elfogadási feltételek: + +- tag nem állíthat vissza; +- visszaállítás megtartja a kapcsolatokat; +- audit nem szerkeszthető, törölhető vagy exportálható; +- rendszerfeladat csak változásnál vagy hibánál jelenik meg. + +### BSS-034 - Scraper és seed importer + +- Méret: `L` +- Függőség: BSS-004, BSS-005, BSS-012, BSS-013, BSS-014 + +Cél: ismételhető, előre kinyert lokális tesztadat létrehozása. + +Tartalom: + +- 50 videó és kapcsolódó események begyűjtése; +- cím, leírás, zene, dátumok, MP4 és thumbnail URL; +- címkék, stábszerepek és kapcsolatok; +- stabil álnevesítés; +- legfeljebb öt párhuzamos kérés; +- retry, backoff és folytatható checkpoint; +- gitignore-olt JSON; +- idempotens seed importer. + +Elfogadási feltételek: + +- médiafájl nem töltődik le; +- email és profilbemutatkozás nem kerül a seedbe; +- újrafuttatás nem duplikál adatot; +- megszakítás után nem indul elölről; +- a seedelt stáblista Authentik tesztprofilokra mutat. + +### BSS-035 - SEO, sitemap és biztonsági headerek + +- Méret: `M` +- Függőség: BSS-019, BSS-021, BSS-022, BSS-023, BSS-024, BSS-026 + +Cél: helyes publikus metaadatok és szűk külső tartalmi engedélyek. + +Tartalom: + +- egyedi title, description, canonical és Open Graph; +- csak publikus tartalmú sitemap; +- robots.txt; +- CSP a `v.bsstudio.hu` és YouTube nocookie forrásokra; +- alap biztonsági headerek; +- admin és keresési technikai oldalak indexelésének tiltása. + +Elfogadási feltételek: + +- korlátozott videó nem kerül sitemapbe vagy metaadatba; +- thumbnail helyesen jelenik meg Open Graph képként; +- CSP mellett a player és YouTube live működik; +- régi slug canonicalként az új útvonalra mutat. + +### BSS-036 - Reszponzív, billentyűzetes és állapotpolírozás + +- Méret: `L` +- Függőség: BSS-020 - BSS-033 + +Cél: egységes, magyar és mobilon is használható teljes rendszer. + +Tartalom: + +- mobil publikus kártyák és admin kártyanézet; +- billentyűzetes kereső és űrlapok; +- fókuszkezelés modáloknál; +- karakterhátralék-jelzés; +- külön üres, betöltési és hibaállapotok; +- média-, Authentik-, jogosultsági és konfliktusüzenetek; +- vizuális egyeztetés a meglévő BSS prototípussal. + +Elfogadási feltételek: + +- kulcsfolyamatok egér nélkül is végigvihetők; +- admin mobilon nem kényszerít használhatatlan táblára; +- hibaüzenet megmondja, mi történt és mi a következő lépés; +- formális WCAG audit nélkül is javul a szemantika és a fókuszkezelés. + +### BSS-037 - Integrációs és végponttól végpontig tesztcsomag + +- Méret: `L` +- Függőség: BSS-020 - BSS-036 + +Cél: a specifikáció 20. fejezetének automatizált lefedése. + +Tartalom: + +- négy hozzáférési szereplő; +- videóállapotok, dátumok, média és lomtár; +- eseménytörlés és kapcsolatbontás; +- címke- és stábszerep-szabályok; +- tagcache és Authentik-kiesés; +- keresési adatszivárgás; +- homepage-prioritás és live; +- elavult mentés; +- slug redirect; +- 30 napos törlés vezérelt órával. + +Elfogadási feltételek: + +- minden elfogadási forgatókönyvhöz van automata teszt vagy indokolt, dokumentált kézi ellenőrzés; +- külső hívások determinisztikus mockot használnak; +- a tesztcsomag tiszta adatbázison indul; +- hiba esetén a teszt megnevezi a sérült üzleti szabályt. + +### BSS-038 - Dokumentáció és lokális átadási próba + +- Méret: `M` +- Függőség: BSS-005, BSS-034, BSS-035, BSS-037 + +Cél: bizonyítani, hogy egy új fejlesztő OOB csomaggal fel tudja építeni és végig tudja mutatni a V0-t. + +Tartalom: + +- README telepítési és indítási lépések; +- OOB fájllista és validáció; +- Authentik bootstrap; +- migráció és seed; +- tesztparancsok; +- szereplőnkénti demo forgatókönyv; +- ismert V0-korlátok és production előtti backlog; +- tiszta klónból végzett átadási próba. + +Elfogadási feltételek: + +- a dokumentált parancsok másolhatóak és működnek; +- hiányzó OOB elem konkrét hibát ad; +- a teljes elfogadási bemutató adatbázis-kézi módosítás nélkül végigvihető; +- a rating, komment, médiafeltöltés, tanfolyamkezelés, production és más kizárt elemek nem csúsztak vissza a V0-ba. + +## 5. Production előtti, külön backlog + +Ezek nem blokkolják a fenti kártyákat: + +- teljes régioldal-migráció; +- Drupal linkek átirányítása; +- production telepítési pipeline; +- UptimeRobot beállítása; +- backup és visszaállítás; +- ténylegesen védett média; +- IP-alapú egyedi nézettség; +- tanfolyam- és felkéréskezelő integráció; +- tömeges adminműveletek; +- formális akadálymentességi audit. diff --git a/docs/implementation-progress.md b/docs/implementation-progress.md new file mode 100644 index 0000000..81eca46 --- /dev/null +++ b/docs/implementation-progress.md @@ -0,0 +1,292 @@ +# BSS V0 implementációs állapot + +Utolsó frissítés: 2026-08-24 (5. fázis vége, fáziskapu zöld) + +## Aktuális fázis + +**5. fázis – Seed, SEO, minőség és átadás: KÉSZ, felhasználói jóváhagyásra vár.** + +Mind az öt kártya (BSS-034 – BSS-038) elkészült, a fáziskapu gate-je zöld. +Ez volt az utolsó végrehajtási hullám; a következő a production előtti, külön +backlog (implementation-plan 5. fejezet), munka csak külön jóváhagyással. + +## Kártyák állapota + +| Kártya | Név | Állapot | Ellenőrzések | +| ------- | ----------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| BSS-001 | Toolchain stabilizálása | done | `pnpm check`, `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build` zöld; nightly/`latest` függőségek eltűntek; dedikált `vitest.config.ts` javítja a Nitro/Vite tesztindítási hibát | +| BSS-002 | Tesztalap és vezérelhető idő | done | unit + integration vitest projektek; `FakeClock` 30 napos törlés szimulációhoz; fetch-mock helper; izolált tesztadatbázis-kezelő (`tests/helpers/test-db.ts`, új: `createMigratedTestDatabase`); integrációs smoke test valódi PostgreSQL-en fut (2× futtatva, ismételhető) | +| BSS-003 | OOB konfigurációs szerződés | done | típusos séma + magyar nyelvű validáció (`src/server/config/oob-schema.ts`); 15 unit teszt; `pnpm check:oob` CLI; dokumentáció: `docs/oob-inputs.md`, példa: `docs/examples/oob-config.example.json` | +| BSS-004 | Új adatbázisséma és migrációs alap | done | 14 tábla + `auth_sessions` migrációja tiszta PostgreSQL-en lefut (CLI és tesztfuttató egyaránt); DB-szintű invariánsok; integrációs sématesztek | +| BSS-005 | Lokális infrastruktúra és Authentik bootstrap | done | compose rendezve (healthcheck, blueprint mount); `pnpm infra:bootstrap` idempotens titok- és YAML-generátor; élőben verifikálva; teljes restart után sem duplikál | +| BSS-006 | OIDC belépés és session | done | `/api/auth/login`, `/api/auth/callback`, `/api/auth/logout` PKCE-s flow-val; DB-ben tárolt session (`auth_sessions`), access token csak szerveroldalon; HTTP-only SameSite=Lax cookie-k; returnTo megőrzés nyitott átirányítás ellenőrzéssel; abszolút 60 perces TTL; Authentik-kiesés magyar 503 oldallal; élő dev szerveren: login 302 az Authentik authorize végpontra, callback hibaág 400, publikus oldal 200 | +| BSS-007 | Jogosultsági policy és guardok | done | viewer szintek (anonymous/schonherz/member/leadership) csoportokból; vezetőség csak tag csoporttal együtt; teljes admin mátrix unit tesztekkel (spec 3.2); `visibleVideoCondition` SQL feltétel valódi adatbázison tesztelve mind a 4 nézői szintre + metaadat-szivárgás ellenőrzés; `requireAdmin`/`requireLeadership` guardok (login redirect returnTo-val / 403); `/api/auth/me` állapotvégpont Authentik-hívás nélkül | +| BSS-008 | Authentik tagcache és szinkron | done | `runMemberSync` client_credentials granthasználatával az Authentik API-ról (lapozott users/groups); mapping konfig szerint; nyers félév megőrzése; ismeretlen státusz → syncStatus=error (publikusból kimarad, utolsó ismert adat megmarad); eltűnt tag rekordja megmarad; változatlan futás NEM ír auditot; szerepváltozás audit előtte-utána párral; `member_sync_runs` állapottábla; kézi indítás csak vezetőségnek; élőben verifikálva a lokális Authentikkel (7 tag cache-elve, idempotens) | +| BSS-009 | Közös slug, audit és optimista zárolás | done | `slugify` magyar ékezet-feloldással; ütközésnél számozott utótag; slug_history lefoglalja a régi slugokat végleges törlés után is; `renameSlugWithHistory` + `resolveSlugRedirect`; `updateWithOptimisticLock`: FOR UPDATE sorzár + verzióellenőrzés (StaleWriteError) + tranzakciós audit előtte-utána értékkel; system aktornál updatedBy NULL; DB-trigger tiltja az audit UPDATE/DELETE-t (custom migráció); plain text és hosszvalidáció (`TEXT_LIMITS`); 14 integrációs teszt | +| BSS-010 | Háttérfeladat-futtató, health, riasztás | done | `JobRegistry` + `dueJobs` FakeClock-kal vezérelt ütemezéssel; `runJobWithLock` PostgreSQL advisory lockkal: két példány közül egy futtat (skipped-locked); induláskori + óránkénti sync feladatok regisztrálva, extra feladatok (lomtár, live) regisztrálhatók; `/health/live` mindig 200, `/health/ready` DB és kulcstábla ellenőrzéssel (503, titok nélkül); hibás háttérfeladat nem dönti le az alkalmazást; vezetőségi szinkronriasztás `getRecentSyncAlerts`; élőben: health 200/200 | +| BSS-011 | Média- és YouTube-validátor | done | host engedélylista (https + v.bsstudio.hu); publikáláshoz HEAD 200 átirányítás nélkül, video/mp4 vs image/* content-type; 405/501-nél egybájtos Range GET tartalék (bytes=0-0, tartalom letöltése nélkül); piszkozathoz hálózat nélküli formaellenőrzés (hibás URL menthető piszkozatban); YouTube normalizálás (watch/live/youtu.be/embed/nocookie) + oEmbed ellenőrzés magyar hibaüzenetekkel; 16 unit teszt | +| BSS-012 | Címke- és stábszerep-domain | done | `src/server/catalog/` (names/tags/staff-roles): normalizált név (kisbetű + whitespace) egyediség; ékezeti hasonlóság csak figyelmeztetés (`findAccentSimilarTagNames`); használt címke törlése csak vezetőséggel + pontos címbeírással; összevonás tranzakcióban kapcsolatvesztés nélkül (ON CONFLICT DO NOTHING); használatban lévő szerep törlésének tiltása + DB restrict; `displayOrder` sorrendezés; minden művelet auditolt; 13 integrációs teszt | +| BSS-013 | Esemény-domain és végleges törlés | done | `src/server/events/domain.ts`: piszkozat csak címmel; publikálás = cím + kezdődátum + (ha van) elérhető thumbnail (BSS-011 validátorral); jövőbeli esemény publikálható; end >= start alkalmazás- és DB-szinten; archiválás → újrapublikálás; slug módosítás előzménnyel; végleges törlés vezetőségi + címbeírással EGY tranzakcióban (videók leválasztása `recordedAt` megőrzésével → régi slug történetbe foglalva → teljes audit `detachedVideoIds`-szal); lista kezdődátum desc, lapozás; 12 integrációs teszt. **Sémaváltozás:** `events.start_date` nullable lett (spec 6.1: piszkozathoz csak cím kell) — migráció: `drizzle/20260823184625_salty_jocasta` | +| BSS-014 | Videó-domain és életciklus | done | `src/server/videos/domain.ts` + `purge.ts` + `highlight-invalidation.ts`: piszkozat csak címmel (hibás média-URL is menthető); publikálás ellenőrzi kötelező mezőket + médiát hálózati validátorral (a tranzakción kívül), többnapos eseménynél `recordedAt`, nem jövőbeli `publishedAt` (múltbeli megadható és megmarad); egynapos esemény csendesen kitölti az üres dátumot, felülírni soha nem írja; leválasztás megtartja a dátumot; intervallumon kívüli dátum csak figyelmeztetés; archiválás/lomtár/visszaállítás (vezetőség, archivált állapotba, kapcsolatokkal); napi 30 napos végleges törlés (`createTrashPurgeJob` a runner extra feladataként, rendszer-audit, slug lefoglalás); kiemelés/Rólunk érvénytelenítés ugyanabban a tranzakcióban állapot- és láthatóságváltozáskor; címkék/stáblista csere verzióellenőrzéssel és auditálással; 14 integrációs teszt | +| BSS-015 | Kapcsolódó videók szolgáltatása | done | `src/server/videos/related.ts`: manuális lista felülír mindent (csak publikált választható, önhivatkozás/duplikátum tiltva); azonos esemény öt legutóbb publikált videója (`publishedAt` desc); esemény nélkül közös címkék pontozással (több közös címke erősebb, egyezésnél `publishedAt` desc, stabil `id` tiebreak); megjelenítés minden ágon `visibleVideoCondition`-nal szűrve az SQL-ben — korlátozott videó metaadata nem szivárog; verzióellenőrzött mentés + audit; 8 integrációs teszt | +| BSS-016 | Megtekintésszámláló | done | `src/server/views/counter.ts`: anonim session cookie (`bss_view_session`) szándékosan Max-Age nélkül — böngésző bezárásáig él; a DB-ben csak a token SHA-256 kivonata, IP/felhasználói előzmény nélkül; `view_sessions` PK + ON CONFLICT DO NOTHING idempotencia párhuzamos kérésekkel szemben (teszt: Promise.all két hívásból, csak egy számol); csak publikált és a nézőnek látható videó számolható; számláló csak admin lekérdezésben (legalább tagság); 7 integrációs teszt | +| BSS-017 | Homepage, live, kiemelés és Rólunk-domain | done | `src/server/homepage/{live,highlight,about,state}.ts`: számított prioritás (aktív live > kiemelt > normál); live ütemezés oEmbed ellenőrzéssel mentéskor is, átfedés alkalmazás- ÉS DB-szinten tiltva (részleges EXCLUDE korlát: `WHERE status <> 'ended'`, így befejezett live fölé mehet másolat) — migráció: `drizzle/20260823191647_melted_mojo` (btree_gist + korlátcsere); `Indítás most` oEmbed-fallbackkel (hiba rögzül, ütemezett marad), `Lezárás most`; befejezett live nem módosítható/törölhető (csak másolatként); perces `createLiveTransitionJob` runner-feladat system audittal; kiemelés csak publikált+publikus videó; Rólunk legfeljebb hat rendezett publikus videó, érvénytelen SQL-ben kiesik; homepage: 5/6 publikus videó (hero nélkül), hat esemény, `Adás hamarosan` 24 órás sáv; 10 integrációs teszt | +| BSS-018 | Kereső-domain | done | `src/server/search/service.ts`: globális keresés videó/esemény/tag/címke csoportokra, súlyozással (spec 11.2); részletes, stabil lapozású videószűrés (`searchVideosDetailed`, címkék ÉS kapcsolattal, `count(*) over()`); láthatóság minden lekérdezésben az SQL-ben (`bss_norm` + pg_trgm, küszöb 0.3); üres/1 karakteres kifejezés nem ér adatbázist; zeneszövegben nincs keresés | +| BSS-019 | Alkalmazásváz, publikus route-ok és hibaoldalak | done | `src/routes/__root.tsx`: magyar 404 (`notFoundComponent`), hiba- és betöltési állapotok; viewer-állapot sessionből (`src/server/pages/viewer.ts` + `fetchViewerState` server fn), navbar Belépés/Kilépés gombbal és mobilmenüvel; publikus slug-feloldás régi slug redirecttel (`src/server/pages/slug-route.ts`) — piszkozat/archív/lomtár/nem látható egységesen 404; route-struktúra: `/videos/$slug`, `/events/$slug`, `/members/$slug`, `/members/archived`, `/members/contributors`, `/search`; magyar üres/betöltési/hibaállapot-komponensek (`PageStates.tsx`); 7 integrációs teszt | +| BSS-020 | Publikus videólista | done | `/videos`: thumbnail+cím kártyák; három rendezés (legutóbb feltöltött/időrendi/legnézettebb); szűrők URL-ben (szabad szöveg, címkék ÉS-kapcsolattal tag-pickerben, esemény slug szerint, recordedAt dátumtartomány, stábtag, stábszerep); oldalméret 10/25/50/100, alap 50; `parseVideoListSearch` ismeretlen értékeknél alapérték-re esik; `getVideoListPage` a BSS-018 szolgáltatást hívja, láthatóság az SQL-ben; szűrőlisták (`getVideoFilterOptions`); magyar üres/betöltési/hibaállapot; 8 integrációs teszt | +| BSS-021 | Videórészlet, player és kapcsolódó tartalom | done | `/videos/$slug`: spec 5.7 blokksorrend (player → cím → készült/feltöltve → eseménylink → leírás → vendégek → zenék → címkék aktív szűrőre linkelve → stáb pozíciónként profil-linkekkel → kapcsolódó videók a BSS-015 szolgáltatásból, SQL-szintű szűréssel); natív player posterrel és `preload="metadata"`, autoplay és letöltés gomb nélkül (`VideoDetailPlayer.tsx`); első `play` → `POST /api/videos/:id/view` (BSS-016 számláló, anonim session cookie, idempotens); médiahiba magyar üzenettel + újrapróbálás; OG/canonical meta; magyar dátumformátum (`formatDateHu`, Europe/Budapest); 7 integrációs teszt | +| BSS-022 | Publikus eseménylista és részletoldal | done | `/events`: kezdődátum desc, csak publikált; kártyán a néző számára látható videók száma overlay-ben; thumbnail fallback = legfrissebb látható videó thumbnailje (DISTINCT ON), majd placeholder; 50-es alaplapozás; `/events/$slug`: dátumintervallum, leírás, videók `recordedAt` desc nulls last, 50-es lapozás; származtatott stáblista csak látható videókból, titulus nélkül, név szerint rendezve; videó nélküli esemény is publikus; 6 integrációs teszt | +| BSS-023 | Publikus taglista és tagprofil | done | `/members`: vezetőség/stúdiósok/jelöltek/jelölt-jelöltek/aktív öregtagok blokkok lapozás nélkül; vezetőségi tag csak a Vezetőség blokkban; `/members/archived` és `/members/contributors` külön aloldalak 50-es lapozással; `/members/$slug` profil (státusz, csatlakozási félév `2023 ősz` formában, bemutatkozás) — email/mobil a sémában sincs; tevékenység év- és szerepnézettel (URL-ben marad), `recordedAt` desc nulls last, 50-es `Továbbiak betöltése`, több szerepnél tudatos ismétlés (`groupActivity` kliensbiztos modulban); csak `sync_status='ok'` profilok publikusak; jogosultsági videószűrés SQL-ben; 6 integrációs teszt | +| BSS-024 | Homepage és YouTube live felület | done | `/`: `getHomepagePage` DTO a BSS-017 state szolgáltatásból; live állapotban youtube-nocookie embed autoplay nélkül; kiemelt hero beágyazott lejátszóval (a cím a videóoldalra visz) + hat, live mellett öt, normálban hat friss videó, hero nem ismétlődik; mindhárom állapot alatt hat esemény; `Adás hamarosan` sáv; percenkénti refetch frissítés nélküli váltással (`refetchInterval: 60_000`); 2 fókuszált integrációs teszt (a prioritás-logika a BSS-017 tesztjeiben) | +| BSS-025 | Globális kereső és találati oldal | done | Navbar popover (`SearchBox.tsx`): 2 karaktertől, 250 ms debounce, csoportonként öt találat, nyíl/Enter/Esc billentyűzetkezelés, Tag/Címke megnevezés nem keveredik; `GET /api/search?q=&limit=` végpont viewer-szintű SQL-szűréssel (tiltott videó metaadata nem kerül válaszba); `/search` fülek Összes/Videók/Események/Tagok, Összes fülön típusonként max 10 találat, Videók fül a részletes lista-szűrést használja query megtartásával, címke-találat `/videos?tags=` aktív szűrőre visz; üres keresés útmutatót ad; 4 integrációs teszt | +| BSS-026 | Rólunk oldal és tanfolyam-átirányítás | done | `/about`: verziókezelt plain text szöveg kódban (`ABOUT_TEXT_VERSION`), legfeljebb hat vezetőségi videó a BSS-017 `getAboutPageVideos`-ból (archivált/lomtári/nem publikus SQL-ben kiesik); `/courses`: szerveroldali 302 a `https://tanfolyam.bsstudio.hu/` címre (`src/server.ts` entry + `isCoursesPath`), kliens navigáció ugyanoda, helyi űrlap és ál-sikerüzenet törölve; 2 integrációs teszt | +| BSS-027 | Adminváz és sidebar | done | `/admin` layout route szerveroldali guarddal: névtelen → belépés megtartott returnTo-val, jogosulatlan → magyar 403 (`ForbiddenContent`); spec 12.1 szerinti sidebar (Tagok/Live/Címkék/Stábszerepek/Audit csak vezetőségnek látja és éri el — közvetlen URL-en is külön guard minden leadership oldalon); belépés után Videók lista nyílik, dashboard nincs; közös `ResponsiveTable` asztali tábla → mobil kártyanézet váltással; magyar admin feliratok (`src/lib/admin-labels.ts`), admin dátumformátum (`formatAdminDateTimeHu`); 9 integrációs teszt (`tests/integration/admin-access.test.ts`) | +| BSS-028 | Videó-adminlista és szerkesztő | done | Admin videólista (spec 12.2 oszlopok) keresés + állapot/láthatóság/esemény/címke szűrőkkel, URL-ben maradó állapottal, 25-es alaplapozással; teljes életciklus API-n: create/update/publish/archive/trash/restore/tags/staff/related (`src/server/api/admin/video-routes.ts` a router diszpécserben); szerkesztő oldal piszkozat/publikálás gombokkal, nincs automatikus mentés, mentetlen változásnál `beforeunload` megerősítés; hibás média-URL piszkozatban menthető, publikálásnál mockolt hálózati ellenőrzéssel tiltva; StaleWriteError → 409 konfliktusbanner „Legfrissebb állapot betöltése" gombbal (sosem utolsó mentés nyer); lejárt session → 401 `loginUrl`, az űrlapadat megőrződik, új belépés után újraküldhető; egynapos esemény csendes dátumkitöltése és dátumwarningok átlátszanak a válaszban; tag nem hozhat létre címkét az űrlapon; 13 integrációs teszt | +| BSS-029 | Esemény-adminlista és szerkesztő | done | Eseménylista (spec 12.3): cím, intervallum, állapot, videószám (minden állapot), módosító+idő; keresés/állapot/dátum szűrők; szerkesztő piszkozat–publikálás–archiválás életciklussal, slug-előzménnyel; végleges törlés csak vezetőségnek, címbeírással, leválasztandó videók számának előzetes megjelenítésével; tömeges művelet nincs; API: create/update/publish/archive/delete_permanent (`src/server/api/admin/event-routes.ts`); 6 integrációs teszt | +| BSS-030 | Címke- és stábszerep-admin | done | Két vezetőségi katalógusoldal: címke létrehozás/átnevezés/összevonás/törlés használati számmal; ékezeti hasonlóság figyelmeztetés `GET /api/admin/tags/similar`-rel; használt címke törlése csak pontos névbeírással (kapcsolatszám előre látszik); stábszerep létrehozás/átnevezés/összevonás/gombos sorrendezés (`displayOrder` mentéssel); használt szerep törlése blokkolva (409 `role_in_use`); tag és schonherz közvetlen API-hívással sem fér hozzá (integrációs teszt bizonyítja); 7 integrációs teszt | +| BSS-031 | Live, kiemelés és Rólunk admin | done | Egy vezetőségi oldal három szekcióval: kiemelt videó kiválasztása/levétele (csak publikált+publikus, korai magyar validációval); live ütemezés YouTube URL normalizálással + oEmbed ellenőrzéssel (hibás azonosító → 400), átfedés kliensen jelzett és szerveren EXCLUDE-korláttal tiltva (409 `overlap`), Indítás most / Lezárás most / törlés / ütemezett élő átütemezés, befejezett live olvasható előzményben, aktiválási hiba rögzítve; Rólunk legfeljebb hat sorrendezett publikus videó, érvénytelen elemek jelölve; tag egyetlen műveletet sem hívhat; 8 integrációs teszt | +| BSS-032 | Rejtett tagdiagnosztika | done | Vezetőségi `/admin/members`: profilok csak olvashatóan (státusz, félév, szinkronállapot, utolsó látvány), tartós szinkronhiba-sáv, utolsó futások listája, kézi szinkron gomb (`POST /api/admin/members/sync` → `triggerManualMemberSync`, auditált, trigger=`manual`), Authentik admin link az OOB issuerből; eltűnt tagok felismerése (lastSeenAt az utolsó sikeres futásnál régebbi); helyi profil-/jogosultságszerkesztés nincs; nyers token nem szivárog a hibaüzenetekbe (tesztelve); 4 integrációs teszt | +| BSS-033 | Lomtár és audit admin | done | `/admin/trash` minden tagnak: lomtári videók törlővel, időponttal, hátralévő napokkal és a purge állapotával; visszaállítás gomb csak vezetőségnek jelenik meg, szerveren is tiltva (tag → 403), restore után archivált állapot, kapcsolatok megmaradnak, elavult verzió 409; `/admin/audit` csak vezetőségnek: szereplő/művelet/entitás/dátum szűrők, lapozás, előtte-utána JSON részletnézet; audit-mutáció útvonal nem létezik (API 404), export/törlés nincs; rendszerfeladatok csak tényleges változásnál írnak auditot (a BSS-010/014 domainjeiből); 5 integrációs teszt | + +| BSS-034 | Scraper eredmény betöltése: seed importer | done | Seed JSON v1 formátum típusos validációval (`src/server/seed/schema.ts`): max 50 videó, tiltott mezők (email/bemutatkozás) elutasítva, médiahost-engedélylista, publikálási előfeltételek, hivatkozási integritás (eventKey/tags/staffRoles) — magyar hibalista helymegjelöléssel; idempotens importer (`src/server/seed/importer.ts`): természetes kulcsok (slug, normalizált név), újrafuttatás nem duplikál és nem ír auditot változatlan entitásra, kapcsolatok determinisztikus szinkronja; stáb a tagcache-beli Authentik sub-hoz kötődik (hiányzó usernél magyar hiba a szinkronról); CLI `pnpm db:seed` (`scripts/import-seed.ts`, config `seed.path`, konkrét hiba hiányzó fájlnál); scraper futtatási szabályai dokumentálva (docs/oob-inputs.md — külön agent-feladat); példa: `docs/examples/seed.example.json`; 9 integrációs teszt | +| BSS-035 | SEO, sitemap és biztonsági headerek | done | Minden publikus videó-, esemény- és tagoldal egyedi title + description + canonical + OG kép/típus leírással (videónál `og:type video.other`, tagnál `profile`); régi slug átirányítás után a canonical az új útvonalra mutat; `/robots.txt` az entryből (Disallow: /admin, /api, /search + Sitemap sor); `/sitemap.xml` (`src/server/pages/sitemap.ts`): statikus útvonalak + publikált ÉS publikus videók + publikált események + `sync_status='ok'` profilok — korlátozott videó metaadata sem kerül bele (integrációs teszt); CSP és alap biztonsági fejlécek (`src/server/http/security-headers.ts`) minden válaszra a `src/server.ts` entryben: media-src/img-src `v.bsstudio.hu`, frame-src youtube-nocookie (+i.ytimg.com thumbnail), nosniff, Referrer-Policy, X-Frame-Options DENY, Permissions-Policy; `/search` és admin noindex; 2 unit + 2 integrációs teszt | +| BSS-036 | Reszponzív, billentyűzetes és állapotpolírozás | done | `AdminTextField` maxLength + karakterhátralék-jelzés (cím/slug/katalógusnevek — a korlátos mezők egységesen jelzik a hátralévő karaktereket, az `AdminTextArea` meglévő képességével együtt); felülvizsgálat: minden admin tábla (`videos`, `events`, `members`, `audit`) `ResponsiveTable`-t használ, a lomtár és a homepage-admin kártyaalapú — mobilon használható; publikus listák (videók, események, tagok, keresés) mind rendelkeznek magyar üres/betöltési/hibaállapotokkal (`PageStates`), a Rólunk oldal címe kiegészült; modáloknál natív `window.confirm` és inline címbeírásos megerősítés van (natív fókuszkezeléssel), nincs egyedi modal; SearchBox nyíl/Enter/Esc kezelése és az űrlapok szabványos elemekből épülnek (billentyűzettel kezelhetők); hibaüzenetek megmondják, mi történt és mi a következő lépés (LoginRequiredBanner belépési linkkel, ConflictBanner újratöltéssel, ValidationProblems listával) | +| BSS-037 | Integrációs és végponttól végpontig tesztcsomag | done | A spec 20. fejezet 17 elfogadási forgatókönyve lefedve: meglévő domain/API/page tesztek + hiánypótló E2E lánc (`tests/integration/e2e-acceptance-chain.test.ts`): tag létrehoz → frissít → publikál a valódi `/api/admin/*` végpontokon session cookie-val, vezetőség kiemel (tag 403), homepage prioritás normal→highlight→(lomtár)normal, hero nem ismétlődő; schönherzes láthatóság anonim homepage-re nem szivárog; külső hívások determinisztikus mockok (`tests/helpers/http-mock.ts`, `reachableMediaFetch`), idő FakeClock; a 17.-hez (tiszta klón) a README dokumentált folyama a kézi ellenőrzés; hibás esetnél a tesztnevek megnevezik a sérült üzleti szabályt | +| BSS-038 | Dokumentáció és lokális átadási próba | done | README átírva magyar átadási dokumentummá: telepítés/indítás a spec 17.2 sorrendjében (függőségek → bootstrap → compose → migráció → OOB ellenőrzés → seed → alkalmazás → minőségi kapu), tesztfelhasználók, szereplőnkénti demo forgatókönyvek (névtelen/schönherzes/tag/vezetőség), ismert V0-korlátok, production előtti backlog, tiszta klónból végzett átadási próba ellenőrzési pontokkal — adatbázis-kézi módosítás nélkül; seed CLI hibaútvonalai élőben ellenőrizve (hiányzó DATABASE_URL és seed fájl konkrét magyar üzenettel) | + +## Fáziskapu eredménye (0. fázis, megtörtént) + +- [x] rögzített, kompatibilis függőségek (lockfile + nincs `latest`/nightly) +- [x] zöld format check, lint, typecheck, test, build +- [x] tiszta adatbázison lefutó migráció +- [x] dokumentált config séma (`docs/oob-inputs.md`) +- [x] idempotens lokális Authentik bootstrap (élőben ellenőrizve) +- [x] mindhárom nézői szint és mindkét adminszerep tesztadata létrehozható + +## OOB bemenetek állapota + +| Fájl | Hely | Állapot | +| ------------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Authentik mapping config | `oob/config.json` (vagy `BSS_OOB_CONFIG`) | lokális változat generált és valid; éles értékek továbbra is OOB | +| Helyi titkok | `.env`, `oob/local-secrets.json`, `oob/authentik.env` | generálva, gitignore-olt, nem kerülnek gitbe | +| Seed JSON | `oob/seed.json` | helye a configban rögzített; formátuma dokumentált és betölthető (`pnpm db:seed`); tartalmát a scraper állítja elő (külön agent-feladat) | + +## Elfogadott felhasználói döntések ebben a fázisban + +Új termékdöntés nincs; a specifikációból következő technikai döntések: + +- A telepített TanStack Start verzió nem támogat server-route fájlokat; helyette testre szabott + `src/server.ts` entry szolgálja ki a `/api/*` és `/health/*` útvonalakat, minden mást az SSR handlernek adva. +- Session modell: `auth_sessions` tábla, a cookie-ban csak véletlen token van, a DB-ben annak SHA-256 hash-e. + Az access token csak a DB-ben él. Abszolút 60 perces lejárat adja a „legfeljebb órás szerepfrissítés” + garanciáját (nincs refresh token, mert a lokális provider nem kér `offline_access` scope-ot). +- Az `auth_sessions.member_sub` NEM idegen kulcs a `member_cache.sub`-ra: így a belépés akkor is működik, + ha a tag még nem szerepel a cache-ben (BSS-008 szinkron feltölti). +- A jogosultsághoz szükséges csoportlistát a szabványos `groups` OIDC claim hordozza; a lokális blueprint + profile scope-ját bővítettük (`scripts/lib/local-bootstrap.ts`), a generált YAML regenerálódott. +- Az OIDC tranzakció (state/PKCE/returnTo) rövid életű, HMAC-SHA256 aláírt cookie-ban utazik; az aláíró + kulcs a config clientId+clientSecret titkából származik (új OOB titok bevezetése nélkül). +- Az id_token signature-ét nem ellenőrizzük (backchannel token), de iss/aud/exp/nonce igen. + +## Fáziskapu eredménye (5. fázis) + +- [x] `pnpm check` zöld +- [x] `pnpm lint` zöld +- [x] `pnpm typecheck` zöld (0 hiba) +- [x] `TEST_DATABASE_URL=... pnpm test` zöld — 44 fájl / 344 teszt (4. fázis vége: 40/328) +- [x] `pnpm build` zöld (nitro .output) +- [x] migráció nem változott (7 migráció maradt; az 5. fázis csak seed, meta, polírozás, teszt és doksi) +- [x] git diff átnézve: titkok csak gitignore-olt `.env` és `oob/` fájlokban; idegen módosítás nincs + +## Elfogadott technikai döntések az 5. fázisban + +- A scraper maga NEM része a repónak (külön agent-feladat); a repóba a + gitignore-olt JSON eredményét betöltő, idempotens seed importer került. + A scraper futtatási szabályait (max 5 párhuzamos kérés, exponenciális + visszalépés 429/5xx-re, oldalanként max 3 próbálkozás, checkpoint) a + docs/oob-inputs.md rögzíti. +- A seed JSON formátumverziója `1`; természetes kulcsok a slug (videó, + esemény) és a normalizált név (címke, stábszerep). Az importer tranzakcióban + dolgozik: változatlan entitásra nem ír (audit sem készül), így az + újrafuttatás valódi idempotencia, nem csak duplikáció-tilalom. +- A seedelt stáblista a lokális Authentik bootstrap tesztprofilok + felhasználónevére hivatkozik; az importer a tagcache-ből oldja fel a sub-ot, + és hiányzó profilnál magyar hibaüzenettel kéri a szinkron lefuttatását — + ezzel a bootstrap sub-jaihoz kötődik, nem keménykódolt azonosítókhoz. +- CSP: a TanStack Start streamelő szkriptjei miatt `script-src 'self' 'unsafe-inline'` + kell (nonce a streaming válaszban nem vezethető be megbízhatóan); + media-src/img-src a `v.bsstudio.hu`, frame-src a youtube-nocookie (+ i.ytimg.com + a live-előnézeti thumbnailök); minden válaszra a `src/server.ts` entryben + érvényesül, az SSR-válasz fejléceit kiegészítve. +- A sitemap közvetlenül az entryből szolgálódik (`/sitemap.xml`), lekérdezése + tesztelhető szervermodulban él (`src/server/pages/sitemap.ts`); csak + publikált + publikus videó, publikált esemény és sikeresen szinkronizált + profil kerül bele; `/robots.txt` ugyanonnan, statikus tartalommal. +- SEO-meta: a videórészlet BSS-021 mintáját követte az esemény- és tagoldal + (canonical az SSR request originjéből a loaderben), leírást az első 300 + karakterből vagy magyar fallback szövegből kapnak. +- Az elfogadási forgatókönyvek lefedettségét a meglévő 40 tesztfájl adja; + új teszt csak a hiányzó E2E lánc és a fázis saját funkciói (seed, sitemap, + headerek) körül készült — meglévő lefedettséget nem duplikáltunk. + +## Fáziskapu eredménye (4. fázis) + +- [x] `pnpm check` zöld +- [x] `pnpm lint` zöld +- [x] `pnpm typecheck` zöld (0 hiba) +- [x] `TEST_DATABASE_URL=... pnpm test` zöld — 40 fájl / 328 teszt (3. fázis vége: 33/276) +- [x] `pnpm build` zöld (nitro .output) +- [x] migráció nem változott (7 migráció maradt; a 4. fázis csak route/UI/API wiring) +- [x] git diff átnézve: titkok csak gitignore-olt `.env` és `oob/` fájlokban; idegen módosítás nincs + +## Elfogadott technikai döntések a 4. fázisban + +- Admin olvasás az oldalmodol-mintával: tesztelhető szervermodulok (`src/server/admin/*.ts`), + a route-ok vékony `createServerFn` GET wrapperekkel hívják őket. +- Admin ÍRÁS `/api/admin/*` JSON végpontokként csatlakozik a `src/server/api/router.ts` + diszpécserhez (a telepített TanStack Start server-route fájlokat továbbra sem támogat). + Minden végpont fogad opcionális `deps.db/deps.config/deps.fetchImpl/deps.clock` paramétert, + így az integrációs tesztek valódi HTTP-kérés objektumokkal, session cookie-val, + determinisztikus mockokkal futnak — ezzel bizonyítható, hogy a tag közvetlen API-hívással + sem ér el vezetőségi erőforrást. +- Egységes domainhiba → JSON hiba fordítás (`src/server/api/admin/http.ts`): + AuthRequiredError → 401+loginUrl, ForbiddenError → 403, StaleWriteError → 409 conflict, + TextValidationError → 400 validation (problems lista), EventConfirmationError/ + ConfirmationMismatchError → 400 confirmation, CatalogNameConflictError → 409 name_conflict, + LiveOverlapError → 409 overlap, StaffRoleInUseError → 409 role_in_use. +- Kliensoldali hívási segéd (`src/lib/admin-api.ts`) diszkriminált eredménnyel dolgozik: + 401-nél megőrzi az űrlapállapotot és loginUrl-t ad (új belépés után újraküldés), + 409-nél konfliktusbanner + „legfrissebb állapot betöltése" gomb. +- Guard-kötés oldalakra: a layout route loaderében `adminAreaAccess` / + `leadershipAreaAccess`; login → `redirect(loginUrl)`, forbidden → `ForbiddenContent`. + A vezetőségi aloldalak (homepage, katalógus, tagok, audit) a saját `beforeLoad`-jukban + ÚJRA guardolnak, így közvetlen URL-en is tiltanak. +- A live-létrehozás és a kiemelés/Rólunk-választás végpontjai korai magyar validációt végeznek + (YouTube oEmbed előszűrés, csak publikált+publikus videó), hogy a domain réteg általános + Error-a helyett géppel olvasható 400-as hiba menjen; a domain tranzakcióin belüli + ellenőrzések érintetlenek maradtak. +- A lomtárlista tartalmazza az aktuális verziót, így a visszaállítás gomb a meglévő + `/api/admin/videos/:id/restore` végpontot hívja optimista verzióellenőrzéssel. +- Tesztinfra: a `createMigratedTestDatabase` poolján `error`-kezelő van (a + `DROP DATABASE ... WITH (FORCE)` lebontási race által okozott alkalmi + unhandled exception eltűnt); a teljes széria kétszer egymás után zöld. + +## Fáziskapu eredménye (3. fázis) + +- [x] `pnpm check` zöld +- [x] `pnpm lint` zöld +- [x] `pnpm typecheck` zöld (0 hiba) +- [x] `TEST_DATABASE_URL=... pnpm test` zöld — 33 fájl / 276 teszt (2. fázis vége: 25/234) +- [x] `pnpm build` zöld (nitro .output) +- [x] migráció nem változott (7 migráció maradt; a 3. fázis csak route/UI/API wiring) +- [x] git diff átnézve: titkok csak gitignore-olt `.env` és `oob/` fájlokban; idegen módosítás nincs + +## Elfogadott technikai döntések a 3. fázisban + +- Az oldallogika tesztelhető szervermodulokban él (`src/server/pages/{video-list,video-detail,event-list,members,homepage,slug-route,viewer}.ts`), + a route-ok `createServerFn` wrapperrel hívják őket; a viewer-t a `getRequest()` + alapján a session cookie-ból oldjuk fel (Authentik-hívás nélkül). +- A kereső és a megtekintésszámláló `/api/*` végpontként csatlakozik a + `src/server/api/router.ts` diszpécserhez (`GET /api/search`, `POST /api/videos/:id/view`); + mindkettő opcionális `deps.db`/`deps.config` paramétert kap az integrációs tesztekhez. +- A `/courses` átirányítás kétféle úton is működik: a `src/server.ts` entry minden + `/courses` kérést 302-vel szolgál ki (JavaScript nélkül is), a kliensoldali + navigációt pedig a route `beforeLoad`-ja kezeli. +- A Rólunk-szöveg verziószámmal jelölt plain text konstans a kódban (spec 10.1: + módosítása kódváltozást igényel). +- A tevékenység-csoportosítás (`groupActivity`) és a magyar dátumformázók + kliensbiztos modulokban élnek (`src/lib/activity.ts`, `src/lib/format-date.ts`), + hogy a route-komponensek ne húzzák be a szervermodulokat. +- A homepage percenkénti `refetchInterval`-lel pollolja a state-et; külön WebSocket + vagy push nem része a V0-nak. + +## Ismert hibák és technikai tartozás (aktuális) + +- `@tanstack/router-cli` (tsr) Node 24-es circular-dependency figyelmeztetése ártalmatlan. +- docker-compose: HTTPS portfeltárás elhagyva (HTTP :9000). +- A navbar globális keresője popoverben max. öt találtat mutat csoportonként; + thumbnail-előnézet a találatokban nincs (nem V0 követelmény). +- Az integrációs tesztek futtatásához továbbra is `TEST_DATABASE_URL` kell (nélküle skipelnek). + +## Fáziskapu eredménye (2. fázis) + +- [x] `pnpm check` zöld +- [x] `pnpm lint` zöld +- [x] `pnpm typecheck` zöld (0 hiba) +- [x] `TEST_DATABASE_URL=... pnpm test` zöld — 25 fájl / 234 teszt (1. fázis: 160) +- [x] `pnpm build` zöld (nitro .output) +- [x] migrációk tiszta adatbázison lefutnak (7 migráció, 16 tábla; EXCLUDE korlát és `bss_norm` függvény ellenőrizve) +- [x] git diff átnézve: titkok csak gitignore-olt `.env` és `oob/` fájlokban; idegen módosítás nincs + +## Új migrációk a 2. fázisban + +| Migráció | Tartalom | +| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `20260823184625_salty_jocasta` | `events.start_date` nullable (spec 6.1: piszkozathoz csak cím kell) | +| `20260823191647_melted_mojo` | live átfedés-tilalom cseréje részleges EXCLUDE korlátra (`WHERE status <> 'ended'`, btree_gist) — befejezett live fölé mehet másolatként új ütemezés | +| `20260823193237_organic_ego` | `pg_trgm` kiterjesztés + `bss_norm(text)` IMMUTABLE SQL függvény (kisbetűs + magyar ékezetlevétel a kereséshez) | + +## Elfogadott technikai döntések a 2. fázisban + +- A domainlogika szerveroldali szolgáltatás-modulokban él (`src/server/{catalog,events,videos,views,homepage,search}`), + route-/UI-wiring a 3–4. fázisban csatlakozik. Minden írás tranzakcióban, auditálva, optimista verzióval. +- `updateWithOptimisticLock` bővítve: opcionális `action` (auditnév) és `afterWrite` callback + (tranzakción belüli kiegészítő írás, pl. homepage-hivatkozás érvénytelenítés). +- Videó életciklus: publikálási médiaellenőrzés (hálózati) szándékosan a tranzakció előtt fut, + hogy ne tartsunk sorzárat hívás közben; az állapotváltás + kiemelés/Rólunk érvénytelenítés egy tranzakció. +- Lomtár-purge és live állapotváltások `JobDefinition`-ként regisztrálhatók a runner extra feladataiként + (`createTrashPurgeJob`, `createLiveTransitionJob`); rendszer-audit csak tényleges változásnál. +- Megtekintésszám: a `videos.view_count` oszlop és a `view_sessions` sorok ugyanabban a tranzakcióban + frissülnek; ON CONFLICT DO NOTHING adja az idempotenciát párhuzamos kérésekkel szemben. +- Keresés: `bss_norm` + pg_trgm `similarity` (küszöb 0.3); a keresőkifejezés paraméterezve megy az SQL-be; + üres/1 karakteres keresés nem ér adatbázist. Részletes videószűrés `count(*) over()`-ral lapoz. +- Keresési súlyok (spec 11.2): pontos 100 > előtag 80 > címke 70/55 > trigram ≤50 > eseménycím 40 > + vendégek/stáb 30/25 > leírás/bemutatkozás 20. + +## Fáziskapu eredménye (1. fázis) + +- [x] `pnpm check` zöld +- [x] `pnpm lint` zöld +- [x] `pnpm typecheck` zöld (0 hiba) +- [x] `TEST_DATABASE_URL=... pnpm test` zöld — 18 fájl / 160 teszt +- [x] `pnpm build` zöld (nitro .output) +- [x] migrációk tiszta adatbázison lefutnak (CLI próba: bss_gate_check, 16 tábla) +- [x] git diff átnézve: titkok csak gitignore-olt oob/ fájlokban; idegen módosítás nincs + +## Elfogadott technikai döntések az 1. fázisban + +- TanStack Start server entry (`src/server.ts`) szolgálja ki a `/api/*` és `/health/*` + útvonalakat; a telepített verziónál a createFileRoute-os server routes még nem elérhetők. +- Session: `auth_sessions` tábla; a cookie-ban véletlen token van, a DB-ben annak + SHA-256 hash-e; abszolút 60 perces TTL adja az órás szerepfrissítést. + Az `auth_sessions.member_sub` NEM idegen kulcs (a belépés cache-szinkron előtt is működjön). +- OIDC tranzakció HMAC-SHA256 aláírt cookie-ban; az aláíró kulcs a clientId+clientSecretből + képzett. Az id_token signature-ét nem ellenőrizzük (backchannel token), de + iss/aud/exp/nonce igen. +- Blueprint változások a szinkronhoz: `sub_mode: user_id` (az API pk-ja = OIDC sub, + így a cache összekapcsolható az API adataival), explicit `grant_types` + (authorization_code + refresh_token + client_credentials), `svc-bss-sync` + szolgáltatási fiók app-password tokennel és csak olvasási jogokkal + (`authentik_core.view_user/view_group`). Új OOB mező: `authentik.sync.{username,token}` + — a docs/oob-inputs.md frissült. +- Tagsági státusz nélküli felhasználók (pl. schönherzesek) NEM kerülnek a tagcache-be; + a korábban ok profil hibás státuszra váltásnál megtartja utolsó adatait, + syncStatus=error jelöléssel (publikus nézetek erre szűrnek). +- Audit immunitás DB-triggerrel védve: UPDATE és DELETE exception-t dob. +- A média HEAD „kapcsolódási” fázisa a fetch API-val nem különíthető el: + egyetlen 15 mp-es teljes timeout van (dokumentált eltérés az 5 mp-es connect + timeout helyett). + +## Parancsok + +| Parancs | Funkció | +| -------------------------------------- | ------------------------------------ | +| `pnpm check` | formátumellenőrzés | +| `pnpm lint` | ESLint | +| `pnpm typecheck` | route-generálás + `tsc --noEmit` | +| `TEST_DATABASE_URL=... pnpm test` | unit + integrációs tesztek | +| `pnpm build` | produkciós build | +| `pnpm db:generate` / `pnpm db:migrate` | migráció generálása / futtatása | +| `pnpm db:seed` | idempotens seed import | +| `pnpm check:oob` | OOB config validáció | +| `pnpm infra:bootstrap` | lokális titkok + Authentik blueprint | diff --git a/docs/oob-inputs.md b/docs/oob-inputs.md new file mode 100644 index 0000000..b50694a --- /dev/null +++ b/docs/oob-inputs.md @@ -0,0 +1,130 @@ +# OOB bemenetek + +Az alkalmazás három bemenete out-of-band, azaz nem a git repóban érkezik. Hiányzó vagy érvénytelen fájl esetén az alkalmazás konkrét hibaüzenettel áll meg, kitalált alapértéket soha nem használ. + +## 1. BSS OOB config + +- Helye alapból: `oob/config.json` (gitignore-olt). +- Felülírható a `BSS_OOB_CONFIG` környezeti változóval. +- Formája: JSON, a szerkezetét a [`docs/examples/oob-config.example.json`](./examples/oob-config.example.json) mutatja. +- Tartalma: + - Authentik OIDC kapcsolat: issuer URL, client id, client secret, scope-ok; + - Authentik tagcache-szinkron szolgáltatási fiókja: `authentik.sync.username` és + `authentik.sync.token` (az `svc-bss-sync` felhasználó app-password tokene, amelyet + a `pnpm infra:bootstrap` generál; a szinkron ezzel client_credentials granttal + fér hozzá az Authentik API-hoz a `goauthentik.io/api` scope-pal); + - claim-leképezés: `sub`, felhasználónév, teljes név, becenév, profilkép URL; + - csoportleképezés: schönherzes, tag és vezetőség csoport neve; + - attribútumleképezés: tagsági státusz nyers érték → belső kulcs, csatlakozási félév értelmezési szabályai, bemutatkozás; + - médiahost-engedélylista (specifikáció szerint `v.bsstudio.hu`); + - YouTube oEmbed végpont; + - seed JSON helye. + +## 2. Helyi titkok + +A titkok (pl. Authentik client secret, session titok) kizárólag OOB fájlokban vagy környezeti változókban élnek: + +- `.env` (gitignore-olt) — pl. `DATABASE_URL`; +- a config fájl `clientSecret` mezője. + +Titok soha nem kerül kliensbundle-be: a config csak szerveroldalon töltődik be. + +Lokális fejlesztésben a titkokat a bootstrap script generálja (`pnpm infra:bootstrap`), a generált fájlok az `oob/` könyvtárba íródnak. + +## 3. Előre kinyert seed JSON + +- Helye: a config `seed.path` mezője által megadott fájl (alapból `oob/seed.json`, gitignore-olt). +- A scraper kimenete; személyes adatot és médiát nem tartalmaz, álneveket használ. +- Formátumverzió: `1`. Példa: [`docs/examples/seed.example.json`](./examples/seed.example.json). + +### Scraper futtatási szabályai (spec 17.1) + +A scraper külön agent-feladat, nem része a repónak. Az eredménye a fenti JSON. +Futtatási követelményei: + +- legfeljebb öt párhuzamos kérés; +- `429` és `5xx` esetén exponenciális visszalépés; +- oldalanként legfeljebb három próbálkozás; +- megszakítás után folytatható checkpoint (nem indul elölről); +- médiafájl nem töltődik le; email és profilbemutatkozás nem kerül a JSON-ba; +- a személyek konzisztens álnevesítése a lokális Authentik bootstrap + tesztprofiljainak felhasználónevére mutat (`oob/local-secrets.json`, + `scripts/lib/local-bootstrap.ts`: `tag-dev`, `vezetoseg-dev`, stb.). + +### Seed JSON formátuma + +```jsonc +{ + "version": 1, + "events": [ + { + "key": "gala-2025", // belső hivatkozási kulcs + "title": "Tavaszi Gála 2025", // kötelező + "slug": null, // opcionális; alapból a keyből slugifyolva + "description": null, + "thumbnailUrl": null, // csak engedélyezett médiahost + "startDate": "2025-05-10", + "endDate": null, + "status": "published", // published|draft|archived, alap: published + }, + ], + "tags": ["Gála", "Adás"], // a videók csak ezeket hivatkozhatják + "staffRoles": ["Operatőr", "Vágó"], + "videos": [ + { + "key": "vid-001", + "title": "Gálanyitó 2025", // kötelező + "slug": null, // alapból a címből slugifyolva + "description": null, + "guests": null, + "songs": null, // soronként „Előadó - Szám címe” + "videoUrl": "https://v.bsstudio.hu/...mp4", + "thumbnailUrl": "https://v.bsstudio.hu/....jpg", + "visibility": "public", // public|schonherz|bss, alap: public + "status": "published", + "recordedAt": "2025-05-10", + "publishedAt": "2025-06-01T12:00:00Z", + "eventKey": "gala-2025", + "tags": ["Gála"], + "staff": [{ "username": "tag-dev", "role": "Operatőr" }], + }, + ], +} +``` + +Szabályok: + +- legfeljebb 50 videó (spec 17.1); +- `email`, `introduction`/`bemutatkozas` mező bármhol tiltott; +- média URL csak `https://` és az OOB config `media.allowedHosts` hostjairól; +- publikált videónál kötelező `videoUrl` és `thumbnailUrl`; +- publikált eseménynél kötelező `startDate`; `endDate >= startDate`; +- a `staff[].username` a tagcache-beli felhasználónévre mutat — a betöltés előtt + futnia kell a tagszinkronnak (alkalmazásindítás vagy kézi szinkron). + +### Betöltés + +```bash +pnpm db:migrate # tiszta séma +pnpm db:seed # a OOB config seed.path fájljának idempotens importja +``` + +Az importer természetes kulcsok (slug, normalizált név) alapján dolgozik: +újrafuttatás nem duplikál, változatlan entitásra nem ír (audit sem készül), +a módosult mezőket és kapcsolatokat szinkronizálja. Megszakadt betöltés után +biztonságosan újrafuttatható. + +## Ellenőrzés + +A config fájl ellenőrzése a repó gyökeréből: + +```bash +pnpm check:oob +``` + +A parancs kiírja az összes hiányzó vagy érvénytelen elemet magyarul, felsorolásosan. Sikeres ellenőrzésnél összefoglalja a betöltött értékeket (a titok nélkül). + +## Viselkedés hiányzó fájl esetén + +- Az alkalmazás indítása megszakad konkrét hibaüzenettel, amely megnevezi a hiányzó fájl elérési útját és az első hibákat. +- Nincs „félig működő” névtelen mód és nincs kitalált mapping. diff --git a/docs/product-specification.md b/docs/product-specification.md new file mode 100644 index 0000000..632e8a2 --- /dev/null +++ b/docs/product-specification.md @@ -0,0 +1,764 @@ +# BSS Website V0 Requirements Specification + +## Document status + +| Field | Value | +| ------------------ | ------------------------------------------------------------ | +| Status | V0 specification prepared on the basis of accepted product decisions | +| Language | English | +| Time zone | Europe/Budapest | +| Target environment | Locally runnable environment with documented startup | +| Production rollout | Not part of V0 | +| Visual basis | The public prototype in the repository | + +This document is the single source of truth for the decisions accepted during consultation. If the current prototype, Figma, or the legacy bsstudio.hu site behaves differently, this specification prevails. + +## 1. Goal and delivery boundary + +V0 is a locally operating video archive and editorial system. In addition to the public pages, the admin workflows must also be usable. Manual database modifications must not stand in for a missing admin feature. + +V0 is considered complete when it can be demonstrated locally with: + +- anonymous, Schönherz-level, and BSS-member viewer access; +- member and board-member admin privileges; +- the full video lifecycle; +- event management; +- member synchronization from Authentik; +- global and detailed search; +- live, featured, and normal homepage priority; +- the audit log and the 30-day video deletion simulation. + +## 2. External inputs + +The following files arrive out-of-band (OOB) and are not committed to git: + +- config containing Authentik attribute and group mapping; +- local secrets; +- pre-extracted seed JSON. + +The README must list the exact location, format, and verification method of these files. When a file is missing, the application must stop with a specific error message. It must not fall back to invented default values. + +## 3. Actors and permissions + +### 3.1 Viewer levels + +| Actor | Visible videos | Admin | +| ------------------------ | ---------------------------- | ---------------------------- | +| Anonymous visitor | `public` | No | +| Logged-in Schönherz user | `public`, `schonherz` | No | +| BSS member | `public`, `schonherz`, `bss` | Yes | +| Board member | `public`, `schonherz`, `bss` | Yes, with extended rights | + +The Authentik `vezetoseg` group complements membership. It does not replace it. + +### 3.2 Admin rights + +| Action | Member | Board member | +| ------------------------------------------------------- | ------ | ------------------------------- | +| Create, edit, publish video and event | Yes | Yes | +| Archive video and event | Yes | Yes | +| Move video to trash | Yes | Yes | +| View video trash | Yes | Yes | +| Restore video | No | Yes | +| Permanently delete event | No | Yes | +| Assign existing tags to a video | Yes | Yes | +| Manage the tag catalog | No | Yes | +| Manage crew roles | No | Yes | +| Manage the credits of one video | Yes | Yes | +| Manage live, featured, and About videos | No | Yes | +| Member list and Authentik sync diagnostics | No | Yes, read-only profiles only | +| View audit log | No | Yes | + +Every member can see and edit other members' drafts. There is no content ownership by author. The server re-checks authorization for every action. + +## 4. Common content rules + +### 4.1 Status and visibility + +Status and visibility are separate data. + +Video and event statuses: + +- `draft`; +- `published`; +- `archived`. + +A video can additionally be moved to `trash` status. Only live uses scheduling. + +Archived content is not visible on the public site. Any member can restore it to published status. A video restored from trash goes into archived status and can then be published separately. + +Video visibility levels: + +- `public`; +- `schonherz`; +- `bss`. + +The default visibility of a new video is `public`. Events and member profiles are public. Their associated videos and derived data must be filtered according to the viewer's access level. + +### 4.2 Identifiers and slugs + +- The internal identifier is a UUID. +- The public path uses a unique slug. +- The slug is derived from the title in lowercase, accent-free, hyphenated form. +- On collision, a numbered suffix is appended. +- The slug can be changed. +- The old slug is kept as a redirect and is never reused even after permanent deletion. +- A member profile's stable internal key is the Authentik `sub` value. The profile's slug is derived from the Authentik username. + +### 4.3 Texts + +All descriptions are plain text with line break support. HTML, Markdown, and rich text are not part of V0. + +| Field | Maximum length | +| -------------------------------- | --------------: | +| Title | 200 chars | +| Slug | 200 chars | +| Tag and crew role | 64 chars | +| Description and bio | 10,000 chars | +| Guests and music used | 5,000 chars | +| URL | 2,048 chars | + +Client and server must enforce the same limits. + +### 4.4 Dates + +- Exact timestamps are stored as UTC timestamps. +- `recordedAt`, as well as an event's start and end, are time-zone-less calendar dates. +- For display, Europe/Budapest applies. +- Public date format: `2026. június 6.` +- Admin and audit format: `2026. június 6. 14:32` +- Event interval: `2026. június 6-8.` +- Joining semester: `2023 ősz` + +## 5. Videos + +### 5.1 Data model + +The fields of a video: + +- UUID and slug; +- title; +- description; +- guests, free text; +- music used, free text; +- MP4 URL; +- thumbnail URL; +- visibility and status; +- `createdAt`, `updatedAt`, `publishedAt`, `recordedAt`; +- view count; +- optional event; +- tags; +- crew members and crew roles; +- ordered manual related videos; +- creator and last modifier. + +The format of the music used is one item per line: + +```text +Előadó - Szám címe +Másik előadó - Másik szám +``` + +A video can belong to at most one event. The event link is optional. + +### 5.2 Date rules + +- `createdAt` is system data and cannot be modified. +- On publication, `publishedAt` receives the current time, but a member can change it to a past point in time. +- Future `publishedAt` values are not allowed. +- For videos without an event, `recordedAt` is optional. +- Assigning a one-day event fills in an empty `recordedAt`. +- For a multi-day event, `recordedAt` must be provided before publishing. +- A date outside the event interval is allowed but triggers a warning. +- Changing the event or the event date does not silently overwrite the video date. +- When detaching an event, `recordedAt` is preserved. + +### 5.3 Draft and publishing + +Only the title is required to save a draft. + +Required for publishing: + +- title; +- valid MP4 URL; +- valid thumbnail URL; +- visibility; +- non-future `publishedAt`; +- `recordedAt` for multi-day events. + +On save, the admin form offers selectable actions: `Save draft` and `Publish`. There is no autosave. Navigating away with unsaved changes requires confirmation. + +### 5.4 Media URLs + +The application does not upload files, does not transcode, and does not delete anything from the media server. It only stores remote URLs. + +For videos and thumbnails: + +- only the `https://v.bsstudio.hu` host is allowed; +- the server sends a `HEAD` request with a 5-second connection timeout and a 15-second total timeout; +- only a `200` response without redirection is accepted; +- videos require `video/mp4` and thumbnails require `image/*` content types; +- `3xx`, `4xx`, timeout, and `5xx` do not allow publishing; +- an invalid or not-yet-verifiable URL can be saved as a draft; +- on `405` or `501`, a one-byte Range GET may be used as a fallback check. + +Visibility only protects page metadata. The external MP4 URL is public, so anyone holding the link can access the file bypassing the application. + +### 5.5 Player and view count + +- Native video controls. +- The thumbnail serves as the poster. +- `preload="metadata"`. +- No autoplay. +- No separate download button. +- No playback position saving. +- On media error, a Hungarian error message and a retry option are displayed. + +The counter increments at the first successful `play` event. One browser session counts the same video once, even across multiple tabs. The session cookie lives until the browser closes. There is no IP address, no per-user viewing history, and no manual counter modification. The view count is visible only in admin. + +### 5.6 Related videos + +Selection order: + +1. ordered manual list, if present; +2. the five most recently published videos of the same event; +3. without an event, the five best videos sharing at least one common tag. + +For common-tag matches, more common tags rank stronger. On ties, descending `publishedAt` decides. + +Manually, any published video can be selected regardless of visibility. Drafts, archived videos, trashed videos, self-references, and duplicates are not allowed. At display time, the viewer's access level always filters. + +### 5.7 Video detail block + +Block order: + +1. player; +2. title; +3. recorded and uploaded dates; +4. event link; +5. description; +6. guests; +7. music used; +8. tags; +9. crew grouped by role; +10. related videos. + +Empty optional blocks are not displayed. A crew member's name links to their member profile. A tag opens the video list with that tag filter active. + +### 5.8 Video list + +The video card shows only a thumbnail and a title. + +Sortings: + +- default and latest uploads: `publishedAt` descending; +- chronological: `recordedAt` descending, missing values at the end; +- most viewed: view count descending. + +On ties, `publishedAt` followed by UUID provides stable ordering. + +Filters: + +- free text; +- multiple tags joined by `AND`; +- event; +- `recordedAt` date range; +- crew member; +- crew role. + +Default page size is 50. Selectable values: 10, 25, 50, 100. Sorting, pagination, and filters persist in the URL. + +## 6. Events + +### 6.1 Data model and publishing + +The fields of an event: + +- UUID and slug; +- title; +- plain-text description; +- optional thumbnail URL; +- start date; +- optional end date; +- status; +- creator, last modifier, and timestamps. + +A draft needs only a title. Publishing requires title and start date. The end date cannot precede the start date. A future event can be published. + +The event thumbnail is optional. If missing, the thumbnail of the newest video visible to the viewer may be used, followed by a placeholder. + +### 6.2 Visibility and derived data + +An event is always public. The video list, the video count, and the credits are built only from videos visible to the viewer. + +There is no separate credits list for an event. The page shows the unique crew members of its videos, sorted by name, without titles. + +### 6.3 List and detail page + +Shown on the event card: + +- thumbnail; +- title; +- the number of videos visible to the viewer in an overlay placed on the thumbnail. + +The event list is sorted descending by start date. Its default page size is 50, with the same selectable sizes as the video list. + +Shown on the event detail page: + +- title, thumbnail, date interval, and description; +- videos sorted descending by `recordedAt`, paginated by 50; +- the derived credits list. + +### 6.4 Permanent deletion + +Only a board member can delete an event. The action is immediate and final. + +Within one transaction: + +1. every video's event link is removed; +2. the event is deleted; +3. a full audit entry is created. + +The videos' `recordedAt` values are preserved. To confirm, the event title must be typed in. + +## 7. Tags and crew roles + +### 7.1 Tags + +There is no separate category system. Multiple tags can belong to a video, and multiple videos to a tag. + +- A member can only assign existing tags to a video. +- A board member can create, rename, merge, and delete tags. +- A tag in use can be deleted after a warning and typing in its name. +- On deletion, all video links are removed. +- On merge, all links move to the target tag. +- Letter case and redundant whitespace must not create duplicates. +- Accents are meaning-distinguishing. The system only warns about names similar when unaccented. + +### 7.2 Crew roles + +Authorization roles and crew roles live in separate tables. + +- Only a board member can create, rename, order, and merge crew roles. +- A role has a `displayOrder` value. +- A role in use cannot be deleted. +- Multiple members can hold the same role on one video. +- A member can hold multiple roles on the same video. + +## 8. Members + +### 8.1 Authentik as source + +Authentik is the single writable source of profile and authorization data. The application's profile pages and admin surface are read-only. + +The OOB config maps: + +- the stable `sub` identifier; +- username; +- full name; +- nickname; +- profile image URL; +- membership status; +- joining year and semester; +- bio; +- `tag` and `vezetoseg` groups. + +The joining semester may arrive as free text. The cache stores the raw value plus the year and the `spring | autumn` value processed according to the config. An unknown format or status causes a sync error. In that case, the profile is not shown on the public list. + +The application neither requests, caches, nor displays email addresses or mobile numbers. + +### 8.2 Cache and synchronization + +- Sync runs at startup, hourly, and on manual trigger by the board. +- Public requests do not call Authentik directly. +- During an Authentik outage, the last cache remains public. +- New logins are impossible; an existing session lives at most one hour. +- A member who has disappeared from Authentik keeps their last known, non-editable record. +- Historical credits and activity are not deleted. +- The hidden board-only surface shows sync status, errors, and the last run. + +### 8.3 Membership statuses + +A person has exactly one membership status: + +- studio member; +- studio candidate; +- candidate candidate; +- active alumnus; +- archived alumnus; +- worked with us before. + +The board role is separate from this. A board member appears only in the Board block and is not repeated in the block of their own status. + +### 8.4 Member list and profile + +Blocks of the active members page: + +1. board; +2. studio members; +3. studio candidates; +4. candidate candidates; +5. active alumni. + +Archived alumni and former contributors get a separate public subpage with pagination of 50. The active members page has no pagination. + +The member card shows a profile image, full name, and nickname. + +Profile order: + +1. profile image, name, and nickname; +2. status and board role; +3. joining semester; +4. bio; +5. activity. + +Activity can switch between year and role views. Both sort descending by `recordedAt`. + +- In year view, groups by crew role appear under each year. +- In role view, videos appear chronologically under each role. +- With multiple roles, the same video appears in every affected group. +- Each view loads 50 videos at a time, then `Load more` continues. +- The view and the selected group persist in the URL. +- Only videos visible to the viewer are shown. + +## 9. Homepage and live + +### 9.1 Priority + +The homepage state is a computed priority: + +1. active live; +2. featured video; +3. normal state. + +In live state, five recent public videos are shown next to the hero. In featured state the featured video plays inline in the hero and six recent public videos are shown next to it; its title links to the video page. The hero video must not repeat in the list. In normal state, six recent public videos are shown. + +In all three states, six events are shown, sorted descending by start date. A future published event may also appear. + +### 9.2 Featuring + +- Only published, public videos can be featured. +- The board selects them. +- Featuring cannot be scheduled. +- Archiving, trashing, or narrowing visibility removes the featuring within the same transaction. + +### 9.3 Live + +Live is a schedule attached to a YouTube video: + +- accepted URL forms: `youtube.com/watch`, `youtube.com/live`, `youtu.be`, YouTube embed; +- the system normalizes the URL to a video ID; +- display uses a `youtube-nocookie.com` embed; +- oEmbed validation runs at save and activation; +- start and end times are required; +- overlapping lives cannot be saved; +- the board gets `Start now` and `Close now` actions; +- no autoplay; +- during the 24 hours before the start, an `Adás hamarosan` banner appears; +- the banner does not replace normal or featured hero content; +- the homepage switches without refresh and checks state once per minute; +- on activation failure, the homepage falls back to featured or normal state; +- a transient YouTube failure does not automatically shut down a running live. + +A finished live does not appear in any public archive. Admin retains a readable history. A past live can only be rescheduled as a copy. + +No video is automatically created from a live. The editor creates a new normal video and may manually assign the `Adás` tag to it. + +## 10. Other public pages + +### 10.1 About + +- The text is version-controlled plain-text content. +- Modifying it requires a code change. +- At the bottom of the page, at most six public videos chosen and ordered by the board are shown. +- Archived, trashed, or non-public videos drop out automatically. + +### 10.2 Course + +The `/courses` path redirects to `https://tanfolyam.bsstudio.hu/` in the same browser tab. + +There is no local course form, data model, admin, export, or email. + +## 11. Search + +### 11.1 Global search box + +The navbar search activates from two characters with a 250 ms delay. It shows at most five results per group and is keyboard-accessible. + +Result types: + +- video, leading to the video detail page; +- event, leading to the event detail page; +- member, leading to the member profile; +- tag, leading to the video list with that filter active. + +In the interface, a person appears as `Tag` and data assigned to videos as `Címke`. + +### 11.2 Weighting + +Importance order: + +1. exact title, name, or nickname; +2. prefix match on title, name, or tag; +3. video tags and event titles; +4. guests and credits; +5. description and bio. + +Music used is not searched. Search ignores letter case and accents. Minor typos are handled by trigram-based similarity. + +For a search query, relevance is the default sorting; on ties, `publishedAt` descending. The user can override this with the video list sortings. + +### 11.3 Full search page + +The `/search` tabs: + +- All; +- Videos; +- Events; +- Members. + +The All tab shows at most ten results per type. Detailed video search uses the accepted filters of the `/videos` page. Dates can only be searched via a dedicated date field; there is no natural-language date recognition. + +An empty search does not dump the database. It shows search guidance and a link to the detailed video filter. + +### 11.4 Access + +The search query already excludes non-visible videos at the database level. Titles, thumbnails, result counts, and other metadata must not leak before client-side post-filtering. + +## 12. Admin interface + +### 12.1 Navigation + +Sidebar items: + +- Videos; +- Events; +- Live and featuring; +- Tag catalog; +- Crew roles; +- Members, board-only and read-only; +- Trash; +- Audit log, board-only. + +There is no separate dashboard. After login, the Videos list opens. The admin is usable on mobile. Complex tables may switch to card views. + +### 12.2 Video list + +Columns: + +- thumbnail and title; +- status; +- visibility; +- event; +- `recordedAt`; +- `publishedAt`; +- view count; +- last modifier and modification time. + +Filters: + +- search; +- status; +- visibility; +- event; +- tag. + +Bulk deletion, archiving, and visibility changes do not exist in V0. + +### 12.3 Event list + +Columns: + +- title; +- date or interval; +- status; +- number of videos; +- last modifier and modification time. + +Filters: search, status, and date. No bulk deletion. + +### 12.4 Concurrent editing + +The application uses optimistic version checking. If someone else modified the record meanwhile, the second save is blocked. The interface shows a conflict message and offers a refresh option. There is no silent last-write-wins behavior. + +When a session expires, the server rejects the save. The client keeps the entered data, requests re-login, and then allows resubmission. + +## 13. Deletion and audit + +### 13.1 Video trash + +- Any member can trash a video after standard confirmation. +- Every member sees the trash and who deleted what and when. +- Only a board member can restore. +- On restore, the video becomes archived. +- Tag, crew, event, and related-video links are preserved in the trash. +- A daily job permanently deletes records that have been in the trash for at least 30 days. +- External media files are not deleted. + +### 13.2 Audit log + +Every creation, modification, publication, archival, trashing, restoration, permanent deletion, and configured admin action is logged. + +Audit contents: + +- Authentik ID or the `system` actor; +- exact time; +- entity type and ID; +- action; +- value before and after the change. + +Only the board sees the log. It is filterable by actor, action, entity, and date. It cannot be deleted or exported and does not offer automatic rollback. Its retention is unlimited. + +System actions write audit entries only on actual changes, errors, or deletions. An unchanged hourly sync does not. + +## 14. Authentik and application security + +- OIDC Authorization Code flow with PKCE. +- Access token never lands in `localStorage`. +- Session lives in an HTTP-only cookie. +- In production the cookie is `Secure`, always `SameSite=Lax`. +- Every server-side query and modification checks authorization. +- Role changes take effect within at most one hour. +- Anonymous users hitting a restricted link get a login prompt with the return URL preserved. +- Logged-in but unauthorized users get a `403` page. +- The title and thumbnail of forbidden videos never enter the HTML. +- Drafts, archived, trashed, permanently deleted, or nonexistent public paths return a consistent `404` page. + +## 15. Background jobs and health + +The application server starts: + +- the hourly Authentik sync; +- the daily video-trash deletion job; +- the live start and end state transitions. + +A PostgreSQL advisory lock prevents parallel double execution. No separate worker or Redis needed. + +Health endpoints: + +- `/health/live`, which checks that the application is running; +- `/health/ready`, which checks the database and migration state. + +Persistent Authentik, live, or media validation failures appear in a board-facing warning banner and a detailed log. No external email or SMS. + +## 16. Routes and metadata + +Public routes: + +- `/videos`; +- `/videos/{slug}`; +- `/events`; +- `/events/{slug}`; +- `/members`; +- `/members/{slug}`; +- separate archived-member and contributor subpages; +- `/about`; +- `/courses`; +- `/search`; +- `/admin`. + +Redirecting the old Drupal `/video`, `/event`, and `/user` links is not part of local V0. It is a separate task before production cutover. + +Every public video, event, and member page gets: + +- a unique title; +- a description; +- a canonical URL; +- an Open Graph image. + +Provide robots.txt and a sitemap listing only public content. There is no separate share button. + +## 17. Seed and local environment + +### 17.1 Scraper + +One agent runs the scraper process. The result is pre-extracted, gitignored JSON. + +Sample content: + +- 50 videos; +- their events and tags; +- titles, descriptions, music, dates, MP4 URLs, and thumbnail URLs; +- crew roles and relationships; +- persons consistently replaced with pseudonyms. + +Profile bios, emails, and media files are not included in the JSON. The scraper has explicit operator permission to ignore the robots.txt crawl delay. + +Execution rules: + +- at most five parallel requests; +- exponential backoff on `429` and `5xx`; +- at most three attempts per page; +- resumable operation after interruption. + +The seed's pseudonyms are represented by the test profiles of the local Authentik bootstrap. + +### 17.2 Startup + +The documented local process: + +1. install dependencies; +2. verify OOB files; +3. start PostgreSQL and Authentik; +4. run clean migrations; +5. run the Authentik blueprint or bootstrap; +6. load the seed; +7. start the application; +8. run typecheck, lint, and tests. + +The current prototype's database schema is disposable. A new, clean migration baseline will be created. + +## 18. Quality requirements + +- Hungarian UI. +- Responsive public and admin pages. +- Mobile and desktop usage. +- Keyboard-operable search and basic forms. +- Accessibility is a useful goal, but formal WCAG compliance is not a V0 release condition. +- Distinct Hungarian empty, loading, and error states. +- Stable, server-side pagination and sorting. +- Typecheck and lint error-free. +- Migrations running on a clean database. +- Integration tests covering authorization, status, search, and deletion flows. +- End-to-end tests covering login, video publishing, event management, and live priority. +- External media and YouTube calls mocked in tests. +- The 30-day deletion testable with a test clock, without real waiting. + +## 19. Out of scope for V0 + +- rating; +- comments; +- share and download buttons; +- IP-based unique views; +- media upload and transcoding; +- actual access protection of MP4 files; +- course form, admin, export, and email; +- request manager; +- public live archive and automatic replay video; +- full migration of the old site; +- redirects for old Drupal links; +- production deployment; +- external UptimeRobot configuration; +- email and mobile fields; +- audit export and automatic rollback; +- bulk admin operations. + +## 20. Acceptance scenarios + +1. An anonymous visitor finds and opens only public videos. +2. A Schönherz user sees public and Schönherz videos and cannot enter admin. +3. A member reaches every visibility level, creates drafts, publishes, archives, and trashes. +4. A board member restores a video, permanently deletes an event, and manages tags and crew roles. +5. An invalid or redirecting media URL can be saved as a draft but not published. +6. A video attached to a one-day event receives an automatic date. For multi-day events, an out-of-range date gives a warning. +7. All three branches of related videos — manual, event-based, and shared-tag — work with permission filtering. +8. An event's video count, video list, and credits leak no restricted video. +9. A member profile's activity can be switched between year and role views and is filtered by permissions. +10. The global search emits no invisible video metadata. +11. The homepage's live, featured, and normal priorities switch without refresh. +12. Overlapping lives cannot be saved; a broken YouTube live fails to activate. +13. Of two concurrent edits, the server blocks the stale save. +14. Restoring from trash preserves relationships and yields archived status. +15. The test clock permanently deletes a video record after 30 days, leaving external media untouched. +16. During an Authentik outage the public cache works; new logins do not. +17. A clean clone can be started with documented steps given the OOB package. diff --git a/drizzle/20260823133645_silent_titanium_man/migration.sql b/drizzle/20260823133645_silent_titanium_man/migration.sql new file mode 100644 index 0000000..dd5fbcb --- /dev/null +++ b/drizzle/20260823133645_silent_titanium_man/migration.sql @@ -0,0 +1,220 @@ +-- Tiszta migrációs alap: a specifikáció szerint a prototípus sémája eldobható. +-- Ez a migráció a régi lokális prototípus-objektumokat eldobja. +-- Csak fejlesztői környezetben fusson! +DROP TABLE IF EXISTS "users_roles" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "videos_events" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "events_roles_users" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "videos_roles_users" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "roles" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "events" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "video_tags" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "related_videos" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "videos" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "tags" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "users" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "current_homepage_status" CASCADE;--> statement-breakpoint +DROP TABLE IF EXISTS "homepage_status" CASCADE;--> statement-breakpoint +DROP TYPE IF EXISTS "homepage_status";--> statement-breakpoint +DROP TYPE IF EXISTS "visibility";--> statement-breakpoint +CREATE EXTENSION IF NOT EXISTS "btree_gist";--> statement-breakpoint +CREATE TYPE "content_status" AS ENUM('draft', 'published', 'archived', 'trash');--> statement-breakpoint +CREATE TYPE "event_status" AS ENUM('draft', 'published', 'archived');--> statement-breakpoint +CREATE TYPE "live_status" AS ENUM('scheduled', 'active', 'ended');--> statement-breakpoint +CREATE TYPE "member_sync_status" AS ENUM('ok', 'error');--> statement-breakpoint +CREATE TYPE "membership_status" AS ENUM('studio_member', 'studio_candidate', 'studio_applicant', 'senior_active', 'senior_archived', 'contributor');--> statement-breakpoint +CREATE TYPE "semester" AS ENUM('spring', 'autumn');--> statement-breakpoint +CREATE TYPE "slug_entity_type" AS ENUM('video', 'event');--> statement-breakpoint +CREATE TYPE "visibility" AS ENUM('public', 'schonherz', 'bss');--> statement-breakpoint +CREATE TABLE "about_page_videos" ( + "position" integer, + "video_id" uuid, + CONSTRAINT "about_page_videos_pkey" PRIMARY KEY("position","video_id"), + CONSTRAINT "about_page_videos_position_range_check" CHECK ("position" >= 1 and "position" <= 6) +); +--> statement-breakpoint +CREATE TABLE "audit_log" ( + "id" bigserial PRIMARY KEY, + "actor" varchar(255) NOT NULL, + "entity_type" varchar(50) NOT NULL, + "entity_id" varchar(255) NOT NULL, + "action" varchar(50) NOT NULL, + "before_value" jsonb, + "after_value" jsonb, + "occurred_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "events" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "slug" varchar(200) NOT NULL, + "title" varchar(200) NOT NULL, + "description" varchar(10000), + "thumbnail_url" varchar(2048), + "start_date" date NOT NULL, + "end_date" date, + "status" "event_status" DEFAULT 'draft'::"event_status" NOT NULL, + "created_by" varchar(255), + "updated_by" varchar(255), + "version" integer DEFAULT 1 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "events_end_after_start_check" CHECK ("end_date" is null or "end_date" >= "start_date") +); +--> statement-breakpoint +CREATE TABLE "live_streams" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "youtube_video_id" varchar(64) NOT NULL, + "starts_at" timestamp with time zone NOT NULL, + "ends_at" timestamp with time zone NOT NULL, + "status" "live_status" DEFAULT 'scheduled'::"live_status" NOT NULL, + "activation_error" text, + "activated_at" timestamp with time zone, + "ended_at" timestamp with time zone, + "created_by" varchar(255), + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "live_streams_end_after_start_check" CHECK ("ends_at" > "starts_at") +); +--> statement-breakpoint +CREATE TABLE "member_cache" ( + "sub" varchar(255) PRIMARY KEY, + "username" varchar(200) NOT NULL, + "full_name" varchar(200) NOT NULL, + "nickname" varchar(200), + "avatar_url" varchar(2048), + "membership_status" "membership_status" NOT NULL, + "is_leadership" boolean DEFAULT false NOT NULL, + "joined_year" integer, + "joined_semester" "semester", + "joined_semester_raw" varchar(100), + "introduction" varchar(10000), + "sync_status" "member_sync_status" DEFAULT 'ok'::"member_sync_status" NOT NULL, + "last_sync_error" text, + "last_seen_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "related_videos" ( + "video_id" uuid, + "related_video_id" uuid, + "position" integer NOT NULL, + CONSTRAINT "related_videos_pkey" PRIMARY KEY("video_id","related_video_id"), + CONSTRAINT "related_videos_no_self_reference_check" CHECK ("video_id" <> "related_video_id") +); +--> statement-breakpoint +CREATE TABLE "site_settings" ( + "id" integer PRIMARY KEY DEFAULT 0, + "highlighted_video_id" uuid, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "slug_history" ( + "entity_type" "slug_entity_type", + "slug" varchar(200), + "entity_id" uuid NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "slug_history_pkey" PRIMARY KEY("entity_type","slug") +); +--> statement-breakpoint +CREATE TABLE "staff_roles" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "name" varchar(64) NOT NULL, + "normalized_name" varchar(64) NOT NULL, + "display_order" integer DEFAULT 0 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "tags" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "name" varchar(64) NOT NULL, + "normalized_name" varchar(64) NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "video_staff" ( + "video_id" uuid, + "role_id" uuid, + "member_sub" varchar(255), + CONSTRAINT "video_staff_pkey" PRIMARY KEY("video_id","role_id","member_sub") +); +--> statement-breakpoint +CREATE TABLE "video_tags" ( + "video_id" uuid, + "tag_id" uuid, + CONSTRAINT "video_tags_pkey" PRIMARY KEY("video_id","tag_id") +); +--> statement-breakpoint +CREATE TABLE "videos" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "slug" varchar(200) NOT NULL, + "title" varchar(200) NOT NULL, + "description" varchar(10000), + "guests" varchar(5000), + "songs" varchar(5000), + "video_url" varchar(2048), + "thumbnail_url" varchar(2048), + "visibility" "visibility" DEFAULT 'public'::"visibility" NOT NULL, + "status" "content_status" DEFAULT 'draft'::"content_status" NOT NULL, + "event_id" uuid, + "recorded_at" date, + "published_at" timestamp with time zone, + "view_count" integer DEFAULT 0 NOT NULL, + "created_by" varchar(255), + "updated_by" varchar(255), + "version" integer DEFAULT 1 NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + "trashed_at" timestamp with time zone, + "trashed_by" varchar(255), + CONSTRAINT "videos_published_requires_timestamp_check" CHECK ("status" <> 'published' or "published_at" is not null), + CONSTRAINT "videos_trash_needs_timestamp_check" CHECK ("status" <> 'trash' or "trashed_at" is not null) +); +--> statement-breakpoint +CREATE TABLE "view_sessions" ( + "video_id" uuid, + "session_id" varchar(128), + "viewed_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "view_sessions_pkey" PRIMARY KEY("video_id","session_id") +); +--> statement-breakpoint +CREATE UNIQUE INDEX "about_page_videos_video_key" ON "about_page_videos" ("video_id");--> statement-breakpoint +CREATE INDEX "audit_log_occurred_idx" ON "audit_log" ("occurred_at");--> statement-breakpoint +CREATE INDEX "audit_log_entity_idx" ON "audit_log" ("entity_type","entity_id");--> statement-breakpoint +CREATE INDEX "audit_log_actor_idx" ON "audit_log" ("actor");--> statement-breakpoint +CREATE INDEX "audit_log_action_idx" ON "audit_log" ("action");--> statement-breakpoint +CREATE UNIQUE INDEX "events_slug_key" ON "events" ("slug");--> statement-breakpoint +CREATE INDEX "events_status_start_idx" ON "events" ("status","start_date");--> statement-breakpoint +CREATE INDEX "live_streams_status_starts_idx" ON "live_streams" ("status","starts_at");--> statement-breakpoint +CREATE UNIQUE INDEX "member_cache_username_key" ON "member_cache" ("username");--> statement-breakpoint +CREATE INDEX "member_cache_status_idx" ON "member_cache" ("membership_status");--> statement-breakpoint +CREATE INDEX "slug_history_entity_idx" ON "slug_history" ("entity_type","entity_id");--> statement-breakpoint +CREATE UNIQUE INDEX "staff_roles_normalized_name_key" ON "staff_roles" ("normalized_name");--> statement-breakpoint +CREATE INDEX "staff_roles_display_order_idx" ON "staff_roles" ("display_order");--> statement-breakpoint +CREATE UNIQUE INDEX "tags_normalized_name_key" ON "tags" ("normalized_name");--> statement-breakpoint +CREATE INDEX "video_staff_member_idx" ON "video_staff" ("member_sub");--> statement-breakpoint +CREATE INDEX "video_staff_role_idx" ON "video_staff" ("role_id");--> statement-breakpoint +CREATE INDEX "video_tags_tag_idx" ON "video_tags" ("tag_id");--> statement-breakpoint +CREATE UNIQUE INDEX "videos_slug_key" ON "videos" ("slug");--> statement-breakpoint +CREATE INDEX "videos_visibility_status_idx" ON "videos" ("visibility","status","published_at");--> statement-breakpoint +CREATE INDEX "videos_event_idx" ON "videos" ("event_id");--> statement-breakpoint +CREATE INDEX "videos_recorded_at_idx" ON "videos" ("recorded_at");--> statement-breakpoint +CREATE INDEX "videos_trash_purge_idx" ON "videos" ("status","trashed_at");--> statement-breakpoint +CREATE INDEX "view_sessions_viewed_idx" ON "view_sessions" ("viewed_at");--> statement-breakpoint +ALTER TABLE "about_page_videos" ADD CONSTRAINT "about_page_videos_video_id_videos_id_fkey" FOREIGN KEY ("video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "events" ADD CONSTRAINT "events_created_by_member_cache_sub_fkey" FOREIGN KEY ("created_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "events" ADD CONSTRAINT "events_updated_by_member_cache_sub_fkey" FOREIGN KEY ("updated_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "live_streams" ADD CONSTRAINT "live_streams_created_by_member_cache_sub_fkey" FOREIGN KEY ("created_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "related_videos" ADD CONSTRAINT "related_videos_video_id_videos_id_fkey" FOREIGN KEY ("video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "related_videos" ADD CONSTRAINT "related_videos_related_video_id_videos_id_fkey" FOREIGN KEY ("related_video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "site_settings" ADD CONSTRAINT "site_settings_highlighted_video_id_videos_id_fkey" FOREIGN KEY ("highlighted_video_id") REFERENCES "videos"("id") ON DELETE SET NULL;--> statement-breakpoint +ALTER TABLE "video_staff" ADD CONSTRAINT "video_staff_video_id_videos_id_fkey" FOREIGN KEY ("video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "video_staff" ADD CONSTRAINT "video_staff_role_id_staff_roles_id_fkey" FOREIGN KEY ("role_id") REFERENCES "staff_roles"("id") ON DELETE RESTRICT;--> statement-breakpoint +ALTER TABLE "video_staff" ADD CONSTRAINT "video_staff_member_sub_member_cache_sub_fkey" FOREIGN KEY ("member_sub") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "video_tags" ADD CONSTRAINT "video_tags_video_id_videos_id_fkey" FOREIGN KEY ("video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "video_tags" ADD CONSTRAINT "video_tags_tag_id_tags_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE CASCADE;--> statement-breakpoint +ALTER TABLE "videos" ADD CONSTRAINT "videos_event_id_events_id_fkey" FOREIGN KEY ("event_id") REFERENCES "events"("id") ON DELETE SET NULL;--> statement-breakpoint +ALTER TABLE "videos" ADD CONSTRAINT "videos_created_by_member_cache_sub_fkey" FOREIGN KEY ("created_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "videos" ADD CONSTRAINT "videos_updated_by_member_cache_sub_fkey" FOREIGN KEY ("updated_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "videos" ADD CONSTRAINT "videos_trashed_by_member_cache_sub_fkey" FOREIGN KEY ("trashed_by") REFERENCES "member_cache"("sub");--> statement-breakpoint +ALTER TABLE "view_sessions" ADD CONSTRAINT "view_sessions_video_id_videos_id_fkey" FOREIGN KEY ("video_id") REFERENCES "videos"("id") ON DELETE CASCADE;--> statement-breakpoint +-- Live időablakok nem fedhetik egymást (specifikáció 9.3). +ALTER TABLE "live_streams" ADD CONSTRAINT "live_streams_no_overlap_excl" EXCLUDE USING gist (tstzrange("starts_at", "ends_at") WITH &&); diff --git a/drizzle/20260823133645_silent_titanium_man/snapshot.json b/drizzle/20260823133645_silent_titanium_man/snapshot.json new file mode 100644 index 0000000..43229a5 --- /dev/null +++ b/drizzle/20260823133645_silent_titanium_man/snapshot.json @@ -0,0 +1,2316 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "eb102e26-e589-497d-aab1-16017519f1ce", + "prevIds": ["00000000-0000-0000-0000-000000000000"], + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823151053_solid_stingray/migration.sql b/drizzle/20260823151053_solid_stingray/migration.sql new file mode 100644 index 0000000..3c9de1a --- /dev/null +++ b/drizzle/20260823151053_solid_stingray/migration.sql @@ -0,0 +1,11 @@ +CREATE TABLE "auth_sessions" ( + "id" varchar(64) PRIMARY KEY, + "member_sub" varchar(255) NOT NULL, + "username" varchar(200) NOT NULL, + "groups" jsonb DEFAULT '[]' NOT NULL, + "access_token" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "expires_at" timestamp with time zone NOT NULL +); +--> statement-breakpoint +CREATE INDEX "auth_sessions_expires_idx" ON "auth_sessions" ("expires_at"); \ No newline at end of file diff --git a/drizzle/20260823151053_solid_stingray/snapshot.json b/drizzle/20260823151053_solid_stingray/snapshot.json new file mode 100644 index 0000000..f6cbd73 --- /dev/null +++ b/drizzle/20260823151053_solid_stingray/snapshot.json @@ -0,0 +1,2442 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "377136ef-6e59-4cd0-8717-f131f79a5f2b", + "prevIds": ["eb102e26-e589-497d-aab1-16017519f1ce"], + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823164832_married_vivisector/migration.sql b/drizzle/20260823164832_married_vivisector/migration.sql new file mode 100644 index 0000000..1cc3a69 --- /dev/null +++ b/drizzle/20260823164832_married_vivisector/migration.sql @@ -0,0 +1,14 @@ +CREATE TYPE "member_sync_trigger" AS ENUM('startup', 'hourly', 'manual', 'test');--> statement-breakpoint +CREATE TABLE "member_sync_runs" ( + "id" bigserial PRIMARY KEY, + "trigger" "member_sync_trigger" NOT NULL, + "status" "member_sync_status" NOT NULL, + "started_at" timestamp with time zone DEFAULT now() NOT NULL, + "finished_at" timestamp with time zone, + "total_count" integer DEFAULT 0 NOT NULL, + "changed_count" integer DEFAULT 0 NOT NULL, + "error_count" integer DEFAULT 0 NOT NULL, + "message" text +); +--> statement-breakpoint +CREATE INDEX "member_sync_runs_started_idx" ON "member_sync_runs" ("started_at"); \ No newline at end of file diff --git a/drizzle/20260823164832_married_vivisector/snapshot.json b/drizzle/20260823164832_married_vivisector/snapshot.json new file mode 100644 index 0000000..4ddef31 --- /dev/null +++ b/drizzle/20260823164832_married_vivisector/snapshot.json @@ -0,0 +1,2600 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "12e7fda7-cad1-4402-b57a-66558828c74d", + "prevIds": ["377136ef-6e59-4cd0-8717-f131f79a5f2b"], + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["startup", "hourly", "manual", "test"], + "name": "member_sync_trigger", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_sync_runs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_trigger", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "total_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "changed_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "error_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_sync_runs_started_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_sync_runs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "member_sync_runs_pkey", + "schema": "public", + "table": "member_sync_runs", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823170852_audit_immutability/migration.sql b/drizzle/20260823170852_audit_immutability/migration.sql new file mode 100644 index 0000000..d0b6f63 --- /dev/null +++ b/drizzle/20260823170852_audit_immutability/migration.sql @@ -0,0 +1,12 @@ +-- Az auditnapló alkalmazásból nem módosítható és nem törölhető (spec 13.2). +CREATE OR REPLACE FUNCTION prevent_audit_mutation() RETURNS trigger AS $$ +BEGIN + RAISE EXCEPTION 'Az auditnapló nem módosítható és nem törölhető'; +END; +$$ LANGUAGE plpgsql; + +--> statement-breakpoint + +CREATE TRIGGER audit_log_no_update +BEFORE UPDATE OR DELETE ON "audit_log" +FOR EACH ROW EXECUTE FUNCTION prevent_audit_mutation(); diff --git a/drizzle/20260823170852_audit_immutability/snapshot.json b/drizzle/20260823170852_audit_immutability/snapshot.json new file mode 100644 index 0000000..e2047b7 --- /dev/null +++ b/drizzle/20260823170852_audit_immutability/snapshot.json @@ -0,0 +1,2600 @@ +{ + "id": "516f562d-d3f2-44bb-bb25-d7a607a92f20", + "prevIds": ["12e7fda7-cad1-4402-b57a-66558828c74d"], + "version": "8", + "dialect": "postgres", + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["startup", "hourly", "manual", "test"], + "name": "member_sync_trigger", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_sync_runs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_trigger", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "total_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "changed_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "error_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_sync_runs_started_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_sync_runs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "member_sync_runs_pkey", + "schema": "public", + "table": "member_sync_runs", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823184625_salty_jocasta/migration.sql b/drizzle/20260823184625_salty_jocasta/migration.sql new file mode 100644 index 0000000..7304456 --- /dev/null +++ b/drizzle/20260823184625_salty_jocasta/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "events" ALTER COLUMN "start_date" DROP NOT NULL; \ No newline at end of file diff --git a/drizzle/20260823184625_salty_jocasta/snapshot.json b/drizzle/20260823184625_salty_jocasta/snapshot.json new file mode 100644 index 0000000..8874dc9 --- /dev/null +++ b/drizzle/20260823184625_salty_jocasta/snapshot.json @@ -0,0 +1,2600 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "024533d1-c675-46af-aed6-b27d15f110ed", + "prevIds": ["516f562d-d3f2-44bb-bb25-d7a607a92f20"], + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["startup", "hourly", "manual", "test"], + "name": "member_sync_trigger", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_sync_runs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_trigger", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "total_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "changed_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "error_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_sync_runs_started_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_sync_runs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "member_sync_runs_pkey", + "schema": "public", + "table": "member_sync_runs", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823191647_melted_mojo/migration.sql b/drizzle/20260823191647_melted_mojo/migration.sql new file mode 100644 index 0000000..d4e656d --- /dev/null +++ b/drizzle/20260823191647_melted_mojo/migration.sql @@ -0,0 +1,3 @@ +ALTER TABLE "live_streams" DROP CONSTRAINT IF EXISTS "live_streams_no_overlap_excl";--> statement-breakpoint +CREATE EXTENSION IF NOT EXISTS "btree_gist";--> statement-breakpoint +ALTER TABLE "live_streams" ADD CONSTRAINT "live_streams_no_overlap_excl" EXCLUDE USING gist (tstzrange("starts_at", "ends_at") WITH &&) WHERE ("status" <> 'ended'); diff --git a/drizzle/20260823191647_melted_mojo/snapshot.json b/drizzle/20260823191647_melted_mojo/snapshot.json new file mode 100644 index 0000000..8e7c1c3 --- /dev/null +++ b/drizzle/20260823191647_melted_mojo/snapshot.json @@ -0,0 +1,2600 @@ +{ + "id": "d2179d05-9513-4b5c-81de-fe2e2cdf2c7f", + "prevIds": ["024533d1-c675-46af-aed6-b27d15f110ed"], + "version": "8", + "dialect": "postgres", + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["startup", "hourly", "manual", "test"], + "name": "member_sync_trigger", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_sync_runs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_trigger", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "total_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "changed_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "error_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_sync_runs_started_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_sync_runs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "member_sync_runs_pkey", + "schema": "public", + "table": "member_sync_runs", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/drizzle/20260823193237_organic_ego/migration.sql b/drizzle/20260823193237_organic_ego/migration.sql new file mode 100644 index 0000000..7e58a1d --- /dev/null +++ b/drizzle/20260823193237_organic_ego/migration.sql @@ -0,0 +1,8 @@ +CREATE EXTENSION IF NOT EXISTS pg_trgm;--> statement-breakpoint +-- Ékezet- és kisbetűfüggetlen keresési forma (spec 11.2): a keresés minden +-- szöveges mezőn ezen a normalizált alapon fut. +CREATE OR REPLACE FUNCTION bss_norm(t text) RETURNS text +LANGUAGE sql IMMUTABLE PARALLEL SAFE +AS $$ + SELECT translate(lower(coalesce(t, '')), 'áéíóöőúüű', 'aeioouuu') +$$; diff --git a/drizzle/20260823193237_organic_ego/snapshot.json b/drizzle/20260823193237_organic_ego/snapshot.json new file mode 100644 index 0000000..32eb4e0 --- /dev/null +++ b/drizzle/20260823193237_organic_ego/snapshot.json @@ -0,0 +1,2600 @@ +{ + "id": "8b3b0891-90aa-480f-b0dd-bb197c427e18", + "prevIds": ["d2179d05-9513-4b5c-81de-fe2e2cdf2c7f"], + "version": "8", + "dialect": "postgres", + "ddl": [ + { + "values": ["draft", "published", "archived", "trash"], + "name": "content_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["draft", "published", "archived"], + "name": "event_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["scheduled", "active", "ended"], + "name": "live_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["ok", "error"], + "name": "member_sync_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["startup", "hourly", "manual", "test"], + "name": "member_sync_trigger", + "entityType": "enums", + "schema": "public" + }, + { + "values": [ + "studio_member", + "studio_candidate", + "studio_applicant", + "senior_active", + "senior_archived", + "contributor" + ], + "name": "membership_status", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["spring", "autumn"], + "name": "semester", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["video", "event"], + "name": "slug_entity_type", + "entityType": "enums", + "schema": "public" + }, + { + "values": ["public", "schonherz", "bss"], + "name": "visibility", + "entityType": "enums", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "about_page_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "audit_log", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "auth_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "events", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "live_streams", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_cache", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "member_sync_runs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "related_videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "site_settings", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "slug_history", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "staff_roles", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_staff", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "video_tags", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "videos", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "view_sessions", + "entityType": "tables", + "schema": "public" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "about_page_videos" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "actor", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(50)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "action", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "before_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "after_value", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "occurred_at", + "entityType": "columns", + "schema": "public", + "table": "audit_log" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'[]'", + "generated": null, + "identity": null, + "name": "groups", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "auth_sessions" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "end_date", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "event_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "events" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "youtube_video_id", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "starts_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ends_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "live_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'scheduled'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activation_error", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "live_streams" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "sub", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "full_name", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "nickname", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "avatar_url", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "membership_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "membership_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_leadership", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_year", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "semester", + "typeSchema": "public", + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(100)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "joined_semester_raw", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "introduction", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'ok'", + "generated": null, + "identity": null, + "name": "sync_status", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_sync_error", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "last_seen_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "member_cache" + }, + { + "type": "bigserial", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_trigger", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trigger", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "member_sync_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "started_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "finished_at", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "total_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "changed_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "error_count", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "message", + "entityType": "columns", + "schema": "public", + "table": "member_sync_runs" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "related_video_id", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "position", + "entityType": "columns", + "schema": "public", + "table": "related_videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "highlighted_video_id", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "site_settings" + }, + { + "type": "slug_entity_type", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_type", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "entity_id", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "slug_history" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "display_order", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "staff_roles" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "normalized_name", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "role_id", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "member_sub", + "entityType": "columns", + "schema": "public", + "table": "video_staff" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tag_id", + "entityType": "columns", + "schema": "public", + "table": "video_tags" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "gen_random_uuid()", + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(200)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "title", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(10000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "description", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "guests", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(5000)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "songs", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(2048)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbnail_url", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "visibility", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'public'", + "generated": null, + "identity": null, + "name": "visibility", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "content_status", + "typeSchema": "public", + "notNull": true, + "dimensions": 0, + "default": "'draft'", + "generated": null, + "identity": null, + "name": "status", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "event_id", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "date", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "recorded_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "published_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "view_count", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "1", + "generated": null, + "identity": null, + "name": "version", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_at", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "trashed_by", + "entityType": "columns", + "schema": "public", + "table": "videos" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "video_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "session_id", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "viewed_at", + "entityType": "columns", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "video_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "about_page_videos_video_key", + "entityType": "indexes", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "occurred_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_occurred_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "actor", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_actor_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "action", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "audit_log_action_idx", + "entityType": "indexes", + "schema": "public", + "table": "audit_log" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "auth_sessions_expires_idx", + "entityType": "indexes", + "schema": "public", + "table": "auth_sessions" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "start_date", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "events_status_start_idx", + "entityType": "indexes", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "starts_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "live_streams_status_starts_idx", + "entityType": "indexes", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_username_key", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "membership_status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_cache_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_cache" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "started_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "member_sync_runs_started_idx", + "entityType": "indexes", + "schema": "public", + "table": "member_sync_runs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "entity_type", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "entity_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "slug_history_entity_idx", + "entityType": "indexes", + "schema": "public", + "table": "slug_history" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "display_order", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "staff_roles_display_order_idx", + "entityType": "indexes", + "schema": "public", + "table": "staff_roles" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "normalized_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "tags_normalized_name_key", + "entityType": "indexes", + "schema": "public", + "table": "tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "member_sub", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_member_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "role_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_staff_role_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tag_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "video_tags_tag_idx", + "entityType": "indexes", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_slug_key", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "visibility", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "published_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_visibility_status_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "event_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_event_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "recorded_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_recorded_at_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "status", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "trashed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "videos_trash_purge_idx", + "entityType": "indexes", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "viewed_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "view_sessions_viewed_idx", + "entityType": "indexes", + "schema": "public", + "table": "view_sessions" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "about_page_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "about_page_videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "events_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "events" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "live_streams_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "live_streams" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["related_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "related_videos_related_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "related_videos" + }, + { + "nameExplicit": false, + "columns": ["highlighted_video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "site_settings_highlighted_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "site_settings" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_staff_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["role_id"], + "schemaTo": "public", + "tableTo": "staff_roles", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "RESTRICT", + "name": "video_staff_role_id_staff_roles_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["member_sub"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "video_staff_member_sub_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_staff" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["tag_id"], + "schemaTo": "public", + "tableTo": "tags", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "video_tags_tag_id_tags_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "video_tags" + }, + { + "nameExplicit": false, + "columns": ["event_id"], + "schemaTo": "public", + "tableTo": "events", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "name": "videos_event_id_events_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["created_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_created_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["updated_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_updated_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["trashed_by"], + "schemaTo": "public", + "tableTo": "member_cache", + "columnsTo": ["sub"], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "name": "videos_trashed_by_member_cache_sub_fkey", + "entityType": "fks", + "schema": "public", + "table": "videos" + }, + { + "nameExplicit": false, + "columns": ["video_id"], + "schemaTo": "public", + "tableTo": "videos", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "view_sessions_video_id_videos_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["position", "video_id"], + "nameExplicit": false, + "name": "about_page_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "about_page_videos" + }, + { + "columns": ["video_id", "related_video_id"], + "nameExplicit": false, + "name": "related_videos_pkey", + "entityType": "pks", + "schema": "public", + "table": "related_videos" + }, + { + "columns": ["entity_type", "slug"], + "nameExplicit": false, + "name": "slug_history_pkey", + "entityType": "pks", + "schema": "public", + "table": "slug_history" + }, + { + "columns": ["video_id", "role_id", "member_sub"], + "nameExplicit": false, + "name": "video_staff_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_staff" + }, + { + "columns": ["video_id", "tag_id"], + "nameExplicit": false, + "name": "video_tags_pkey", + "entityType": "pks", + "schema": "public", + "table": "video_tags" + }, + { + "columns": ["video_id", "session_id"], + "nameExplicit": false, + "name": "view_sessions_pkey", + "entityType": "pks", + "schema": "public", + "table": "view_sessions" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "audit_log_pkey", + "schema": "public", + "table": "audit_log", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "auth_sessions_pkey", + "schema": "public", + "table": "auth_sessions", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "events_pkey", + "schema": "public", + "table": "events", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "live_streams_pkey", + "schema": "public", + "table": "live_streams", + "entityType": "pks" + }, + { + "columns": ["sub"], + "nameExplicit": false, + "name": "member_cache_pkey", + "schema": "public", + "table": "member_cache", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "member_sync_runs_pkey", + "schema": "public", + "table": "member_sync_runs", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "site_settings_pkey", + "schema": "public", + "table": "site_settings", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "staff_roles_pkey", + "schema": "public", + "table": "staff_roles", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "tags_pkey", + "schema": "public", + "table": "tags", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "videos_pkey", + "schema": "public", + "table": "videos", + "entityType": "pks" + }, + { + "value": "\"position\" >= 1 and \"position\" <= 6", + "name": "about_page_videos_position_range_check", + "entityType": "checks", + "schema": "public", + "table": "about_page_videos" + }, + { + "value": "\"end_date\" is null or \"end_date\" >= \"start_date\"", + "name": "events_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "events" + }, + { + "value": "\"ends_at\" > \"starts_at\"", + "name": "live_streams_end_after_start_check", + "entityType": "checks", + "schema": "public", + "table": "live_streams" + }, + { + "value": "\"video_id\" <> \"related_video_id\"", + "name": "related_videos_no_self_reference_check", + "entityType": "checks", + "schema": "public", + "table": "related_videos" + }, + { + "value": "\"status\" <> 'published' or \"published_at\" is not null", + "name": "videos_published_requires_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + }, + { + "value": "\"status\" <> 'trash' or \"trashed_at\" is not null", + "name": "videos_trash_needs_timestamp_check", + "entityType": "checks", + "schema": "public", + "table": "videos" + } + ], + "renames": [] +} diff --git a/eslint.config.js b/eslint.config.js index 3f272c0..319666d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -15,6 +15,14 @@ export default [ }, }, { - ignores: ['eslint.config.js', 'prettier.config.js'], + ignores: [ + 'eslint.config.js', + 'prettier.config.js', + 'dist/**', + '.output/**', + '.nitro/**', + '.tanstack/**', + 'node_modules/**', + ], }, ] diff --git a/package.json b/package.json index d4a6e6a..e9d9376 100644 --- a/package.json +++ b/package.json @@ -10,32 +10,35 @@ "generate-routes": "tsr generate", "build": "vite build", "preview": "vite preview", + "typecheck": "tsr generate && tsc --noEmit", "test": "vitest run", "lint": "eslint", "format": "prettier --write . && eslint --fix", "check": "prettier --check .", - "push": "npx drizzle-kit push", - "generate": "npx drizzle-kit generate", - "migrate": "npx drizzle-kit migrate" + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:seed": "tsx scripts/import-seed.ts", + "check:oob": "tsx scripts/check-oob.ts", + "infra:bootstrap": "tsx scripts/bootstrap-local.ts" }, "dependencies": { "@base-ui/react": "^1.6.0", "@fontsource-variable/geist": "^5.2.9", "@tailwindcss/vite": "^4.1.18", - "@tanstack/react-devtools": "latest", - "@tanstack/react-query": "latest", - "@tanstack/react-query-devtools": "latest", - "@tanstack/react-router": "latest", - "@tanstack/react-router-devtools": "latest", - "@tanstack/react-router-ssr-query": "latest", - "@tanstack/react-start": "latest", - "@tanstack/router-plugin": "^1.132.0", + "@tanstack/react-devtools": "^0.10.12", + "@tanstack/react-query": "^5.102.1", + "@tanstack/react-query-devtools": "^5.102.1", + "@tanstack/react-router": "^1.170.32", + "@tanstack/react-router-devtools": "^1.167.1", + "@tanstack/react-router-ssr-query": "^1.167.1", + "@tanstack/react-start": "^1.168.49", + "@tanstack/router-plugin": "^1.168.35", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^17.4.2", "drizzle-orm": "1.0.0-rc.4", "lucide-react": "^0.545.0", - "nitro": "npm:nitro-nightly@latest", + "nitro": "3.0.260610-beta", "pg": "^8.22.0", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -46,9 +49,9 @@ }, "devDependencies": { "@tailwindcss/typography": "^0.5.16", - "@tanstack/devtools-vite": "latest", - "@tanstack/eslint-config": "latest", - "@tanstack/router-cli": "^1.132.0", + "@tanstack/devtools-vite": "^0.8.5", + "@tanstack/eslint-config": "^0.4.0", + "@tanstack/router-cli": "^1.167.33", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.0", "@types/node": "^22.10.2", @@ -64,11 +67,5 @@ "typescript": "^6.0.2", "vite": "^8.0.0", "vitest": "^4.1.5" - }, - "pnpm": { - "onlyBuiltDependencies": [ - "esbuild", - "lightningcss" - ] } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27e6eca..542f590 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,29 +18,29 @@ importers: specifier: ^4.1.18 version: 4.3.2(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) '@tanstack/react-devtools': - specifier: latest - version: 0.10.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14) + specifier: ^0.10.12 + version: 0.10.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14) '@tanstack/react-query': - specifier: latest - version: 5.101.2(react@19.2.7) + specifier: ^5.102.1 + version: 5.102.1(react@19.2.7) '@tanstack/react-query-devtools': - specifier: latest - version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(react@19.2.7) + specifier: ^5.102.1 + version: 5.102.1(@tanstack/react-query@5.102.1(react@19.2.7))(react@19.2.7) '@tanstack/react-router': - specifier: latest - version: 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.170.32 + version: 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-router-devtools': - specifier: latest - version: 1.167.0(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.15)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.167.1 + version: 1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-router-ssr-query': - specifier: latest - version: 1.167.1(@tanstack/query-core@5.101.2)(@tanstack/react-query@5.101.2(react@19.2.7))(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.15)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^1.167.1 + version: 1.167.1(@tanstack/query-core@5.102.1)(@tanstack/react-query@5.102.1(react@19.2.7))(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.27)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/react-start': - specifier: latest - version: 1.168.28(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + specifier: ^1.168.49 + version: 1.168.49(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) '@tanstack/router-plugin': - specifier: ^1.132.0 - version: 1.168.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + specifier: ^1.168.35 + version: 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -57,8 +57,8 @@ importers: specifier: ^0.545.0 version: 0.545.0(react@19.2.7) nitro: - specifier: npm:nitro-nightly@latest - version: nitro-nightly@4.0.0-20251010-091516-7cafddba(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76))(lru-cache@11.5.2)(rolldown@1.1.5)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + specifier: 3.0.260610-beta + version: 3.0.260610-beta(chokidar@5.0.0)(dotenv@17.4.2)(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76))(jiti@2.7.0)(lru-cache@11.5.2)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) pg: specifier: ^8.22.0 version: 8.22.0 @@ -70,7 +70,7 @@ importers: version: 19.2.7(react@19.2.7) shadcn: specifier: ^4.13.0 - version: 4.13.0(typescript@6.0.3) + version: 4.13.0(supports-color@7.2.0)(typescript@6.0.3) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -85,14 +85,14 @@ importers: specifier: ^0.5.16 version: 0.5.20(tailwindcss@4.3.2) '@tanstack/devtools-vite': - specifier: latest - version: 0.8.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + specifier: ^0.8.5 + version: 0.8.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) '@tanstack/eslint-config': - specifier: latest - version: 0.4.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + specifier: ^0.4.0 + version: 0.4.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) '@tanstack/router-cli': - specifier: ^1.132.0 - version: 1.167.19 + specifier: ^1.167.33 + version: 1.167.33(supports-color@7.2.0) '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 @@ -119,10 +119,10 @@ importers: version: 1.0.0-rc.4 eslint: specifier: ^9.20.0 - version: 9.39.5(jiti@2.7.0) + version: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) jsdom: specifier: ^28.1.0 - version: 28.1.0 + version: 28.1.0(supports-color@7.2.0) prettier: specifier: ^3.8.1 version: 3.9.5 @@ -137,7 +137,7 @@ importers: version: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) vitest: specifier: ^4.1.5 - version: 4.1.10(@types/node@22.20.1)(jsdom@28.1.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + version: 4.1.10(@types/node@22.20.1)(jsdom@28.1.0(supports-color@7.2.0))(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) packages: @@ -838,6 +838,15 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@neodrag/core@3.0.0-next.11': + resolution: {integrity: sha512-3WQWxyrbxiaK9zS5JU2wJsW2gpoQlZBXVghduBh61JpqaeE0T0cte8R0qYK2RuJo3J2TYQYqxO19CpG/C1i5eg==} + + '@neodrag/solid@3.0.0-next.11': + resolution: {integrity: sha512-vCBIn/pimjWMQ6vhTS2/O1XNAwzVtc4eUhdbQ91WykbZWWqQ5NocDXt/1OdYrEkeRzJcpCv8wEz5PnMkgKP81Q==} + peerDependencies: + '@neodrag/core': 3.0.0-next.11 + solid-js: ^1.0.0 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1380,12 +1389,16 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 + '@tanstack/devtools-bundler-core@0.1.3': + resolution: {integrity: sha512-F0tlxIyfFqXkZ1mJP1EjtkiSeJA+ztXY2AYOHf7r4goCIEAOp86N9PFJ/yv8vu1TnmxGS6vKsZCS3Kyls9xQQA==} + engines: {node: '>=18'} + '@tanstack/devtools-client@0.0.8': resolution: {integrity: sha512-cG3iZkGWCwN330bLBKa8+9r4Of2AXNoz2zUqcsy/4XsD3105ghVBx78cGyvJj9fSclNomPxoqAnDGXXhg1WLvA==} engines: {node: '>=18'} - '@tanstack/devtools-event-bus@0.4.2': - resolution: {integrity: sha512-2LHzhwBFlKHCcklsQrGe8TeyjHd4XAF8nuCO6wHmva5fePUkJUULbu6CsCNAlGlCi0KkEsMXZSvRdR4HgMq4yA==} + '@tanstack/devtools-event-bus@0.4.3': + resolution: {integrity: sha512-NeegBt5/n2E5q4DbrXHqECBq42+kDi6JBOp8/+RNqkIE+P4hJpbM36kGyjnGQFMWOoku31qhMyX9/48VuTTdmg==} engines: {node: '>=18'} '@tanstack/devtools-event-client@0.5.0': @@ -1393,21 +1406,21 @@ packages: engines: {node: '>=18'} hasBin: true - '@tanstack/devtools-ui@0.6.0': - resolution: {integrity: sha512-CVaM6rT6Nl5ijo83vJYFa2SjofvpuOl/uOvbYGhBrRgUhhelNHhx8zZX+hnZCHmIr0/lzM65hsocnZ72592Rvg==} + '@tanstack/devtools-ui@0.7.1': + resolution: {integrity: sha512-3xQ/ezZ2qVNszhjpCN2N3jn7uHc2J1PMgcyjHzH4XZBt9xAQyMMcPNoR2cd7rzReyxWoJpZUWiDBmOJiCtLj9A==} engines: {node: '>=18'} peerDependencies: solid-js: '>=1.9.7' - '@tanstack/devtools-vite@0.8.1': - resolution: {integrity: sha512-oQxOo0fI0bwhHtw/psFlIR0OS/bsKrirBxwnw2vuhCM4bjt3k4EZZsW/lvZ1+Vpouhts7LSyvngnxvGXbQ1sUQ==} + '@tanstack/devtools-vite@0.8.5': + resolution: {integrity: sha512-xaifCEmiwwzizlbp973oISXNsnmuU/BugLa66gryAZaPJJ/qo1kJd2DxY5X960eHwmyIS3SN0KYrL3/aRhucmA==} engines: {node: '>=18'} hasBin: true peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@tanstack/devtools@0.12.5': - resolution: {integrity: sha512-JdxTSeVdjJheycgz4c7qbldNKDCEDWlWr1l9dZBhd9sOmRBT5Z70ka9Eb8mb+FUnalcOIB62IDSR/iSxAIUD8Q==} + '@tanstack/devtools@0.14.2': + resolution: {integrity: sha512-8FVVmDU+x3iEwrl5rtbIhud8gwp9eSXPlhs36SCV59yAtXmheFFvLqLAUXpfIU79sZVBg3LLTy2VlTIXaWbaBw==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -1419,18 +1432,18 @@ packages: peerDependencies: eslint: ^9.0.0 || ^10.0.0 - '@tanstack/history@1.162.0': - resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==} + '@tanstack/history@1.162.1': + resolution: {integrity: sha512-DR9t6lfLVdrjgCwpglrR9DR7Ok8/HlXjcOE+goWXF3zyuLUO/ug7vMbSFxTqrQTtbRghJfyhmIZ0S6LhPIy44w==} engines: {node: '>=20.19'} - '@tanstack/query-core@5.101.2': - resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} + '@tanstack/query-core@5.102.1': + resolution: {integrity: sha512-bJTsrO2ODWSgg+x1leTsK036GBtmNNjnrF1Vk06J5fg/phYZLQIdXrBe0uf5xTItXYvIW1MtbLhyPw7cPMqUUQ==} - '@tanstack/query-devtools@5.101.2': - resolution: {integrity: sha512-o+wHcqgN7Pp0s8v1i0UGq/ZrrEKrxdIiMQmKRdYb2w7NPtylYSJ4+wg/tIn71m9DLstwUwdEGAvROdly6HXP6w==} + '@tanstack/query-devtools@5.102.1': + resolution: {integrity: sha512-zX3fv4ZpkK+5Wzw0V2Axk1z6d1jMfMGgRdLVkF6BCJwZt8Iqveboy1G8rXV1o4cQ4ptKpMS45iOWPHape4QXoQ==} - '@tanstack/react-devtools@0.10.8': - resolution: {integrity: sha512-YJV6YttQf9lhhPbPBLULgy1eScEvJUMsCS26mjg9hfBKgAJQA5sF9zvzorDzW9Ob6o/asoXikO81JnRUVuFX0Q==} + '@tanstack/react-devtools@0.10.12': + resolution: {integrity: sha512-dgoz7TFm97Izo/D34z91PD0h+ufk+eBmoN9OgRHJlj/c7Ol5xpIP7bqLBNByjgt5paRE2eSu5AQHV92Ul+G6iw==} engines: {node: '>=18'} peerDependencies: '@types/react': '>=16.8' @@ -1438,23 +1451,23 @@ packages: react: '>=16.8' react-dom: '>=16.8' - '@tanstack/react-query-devtools@5.101.2': - resolution: {integrity: sha512-eU7HctdA9gDjqoERoEdzLbw9DiqnBDfh5+Hu0u26gjqoHJezOpQAuiesDL2VvkU+2cPV76zgv0tMZsOrI4LjnQ==} + '@tanstack/react-query-devtools@5.102.1': + resolution: {integrity: sha512-4ZrumxXJyt3va+L85M5g/ppBU5LfBrl01IbxmVU9M4Ti/b+2O9E76emulZRsOjRS6AGtdDFQnMJnmYA/fGga0A==} peerDependencies: - '@tanstack/react-query': ^5.101.2 + '@tanstack/react-query': ^5.102.1 react: ^18 || ^19 - '@tanstack/react-query@5.101.2': - resolution: {integrity: sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==} + '@tanstack/react-query@5.102.1': + resolution: {integrity: sha512-DKJeRvxjsGUWhUVnVVXSseTS0SKBMLSkrz8dm7i08r93IQQ2gAfUkX1e801gVZ18KSwHiG5yng4tQm5+qSGS0w==} peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.167.0': - resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} + '@tanstack/react-router-devtools@1.167.1': + resolution: {integrity: sha512-pjfGrmjj4d7naEPM7oshqFfwBoxDPNo/UxltlHH5ePbHsJ+plBhd+JaAewm1ueYOjZ0js9hckjWWDYXpCrSfKw==} engines: {node: '>=20.19'} peerDependencies: - '@tanstack/react-router': ^1.170.0 - '@tanstack/router-core': ^1.170.0 + '@tanstack/react-router': ^1.170.19 + '@tanstack/router-core': ^1.171.16 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' peerDependenciesMeta: @@ -1471,26 +1484,26 @@ packages: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.170.18': - resolution: {integrity: sha512-wpbGYZEp/fmz1q4bn7BD8VZ+/VZ7GBqSJv5V969pU+chP8y7dquWDmKTFMohvUegb9lg12m1uPVvD6kB2wORvQ==} + '@tanstack/react-router@1.170.32': + resolution: {integrity: sha512-SIpxvaTKco100a5ZR3ePmArbhtm3XOx+w1dpGYY9gxHDta4iXSKDdQuhLonwJbIMkVJsU1rwXf0UDHMrF/1snw==} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-client@1.168.16': - resolution: {integrity: sha512-1OfHgy0wpHwe2tlB3FxMeA+IMX6Il/QAMf+8UdXuimReIc2Lz3BkMLBL38k4GIxBguX9sI8EMLO5jlTZ4e1olw==} + '@tanstack/react-start-client@1.168.30': + resolution: {integrity: sha512-qsZuykUl1EF0/rc1bin1RtjFzz07YMOTBzhOstSDzbOVm/WKf1QKFTN+qAZi74xlbXSWNuT2MiFRyg4RKwj8iw==} engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-rsc@0.1.27': - resolution: {integrity: sha512-4Gt8+XHRhcYJjfw1rTlkW5ZxRWcp6AmvPoBf1p7ysOSVlnfVitQBqgAAFNGy+d745vWfoz1/JWqyFBP8IVVYYg==} + '@tanstack/react-start-rsc@0.1.48': + resolution: {integrity: sha512-UglRdTMuF3c4dvzL/gh4dMVbMWHsPy8ZgTQdT2qpTlyt1b/3m+R40tBFdsfTgw76VTXivW+qV/ih0PN9000XTw==} engines: {node: '>=22.12.0'} peerDependencies: '@rspack/core': '>=2.0.0-0' - '@vitejs/plugin-rsc': '>=0.5.20' + '@vitejs/plugin-rsc': '>=0.5.30' react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' react-server-dom-rspack: '>=0.0.2' @@ -1502,15 +1515,15 @@ packages: react-server-dom-rspack: optional: true - '@tanstack/react-start-server@1.167.22': - resolution: {integrity: sha512-eH2PeHuLfL3R5YzE9+y2FfcE4Ld1LNV2ZfrCNVPJMMJFt+9nXDaRHg9BsEmc+JkTAGzz3FKLyQEoWwpbG6Ehqg==} + '@tanstack/react-start-server@1.167.37': + resolution: {integrity: sha512-cODHpFU8vIm7AdHii9W3NEuwyruNmT5wLDZjRsJLT9Jp+7FACnfJrRbvxnp1ldSv/9mxcHKi/OgwbdHQeMZcAQ==} engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start@1.168.28': - resolution: {integrity: sha512-UyJ/OYBw7x8MQclyXKlbg72qzSTK6Do4AugHcx0LoagHNg6tQgvF0l2HZqll2CrxKAlv2Ar8+0gEkHnFYxxI5Q==} + '@tanstack/react-start@1.168.49': + resolution: {integrity: sha512-iQb1ZoEHqvZMGLR4G7v3tTtgL06yv/bwxvGP3waVHxVn7bRpyopM44YbOluaGkcJzc13ZTvdLKWYNAN3bxMX/Q==} engines: {node: '>=22.12.0'} peerDependencies: '@rsbuild/core': ^2.0.0 @@ -1532,35 +1545,35 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-cli@1.167.19': - resolution: {integrity: sha512-QdgcIJ/BBaMD94RRnkUFrwOnDe5cO3fXbt3p3DklEqUCI1PpTkt7DssRsL0Hf01y0gJU7Jm1KHE4/Am72BcOZQ==} + '@tanstack/router-cli@1.167.33': + resolution: {integrity: sha512-OZcP4zmzj85rLq2Cr6I3tZDT9Yb8gsquDgka+baKOKXJULBqP5uZ56X8Ggrgq5IBypk47/b1K2tA1qChd1qUig==} engines: {node: '>=20.19'} hasBin: true - '@tanstack/router-core@1.171.15': - resolution: {integrity: sha512-IILCDcLaItMZQ2jEmCABHY1Nhjjn5XUvwpQp3e4Nmu+vfg0BgYFuu/QASz2SwE2ZNbVMrvt8X/wxa+Gg5aErxA==} + '@tanstack/router-core@1.171.27': + resolution: {integrity: sha512-wDwSLvoLwIaNcnx9UNcN9Mb7Y8QwCYq1U1RQZwyN186gnkIoIYI2SOxy8VqH1vFigbkHkk4FmwMAQlghPgDK2g==} engines: {node: '>=20.19'} - '@tanstack/router-devtools-core@1.168.0': - resolution: {integrity: sha512-wQoQhlBK7nlZgqzaqdYXKWNTpdHdsaREdaPhFZVH0/Ador+F+eM3/NF2i3f2LPeS0GgKraZUQXe1Q/1+KHyEYg==} + '@tanstack/router-devtools-core@1.168.1': + resolution: {integrity: sha512-qr4voa4cpSMwQvS3867xkU3AB3MtJbTuovKIy+btjJ/Faju6er9w0nDylmD+005Mk/3YKw9/iueZJl2JAB7JOA==} engines: {node: '>=20.19'} peerDependencies: - '@tanstack/router-core': ^1.170.0 + '@tanstack/router-core': ^1.171.16 csstype: ^3.0.10 peerDependenciesMeta: csstype: optional: true - '@tanstack/router-generator@1.167.19': - resolution: {integrity: sha512-db3AQGLinf8ZQ8AKMyxLVWwHIFZxtx+zLktvPXv/nFH/B793/Z7lKLl4dUWM3JpAluynDSVVKaTWQVTAgTYmVA==} + '@tanstack/router-generator@1.167.33': + resolution: {integrity: sha512-Z3lCWIPuRUMPmuI8Mm48x/s49TxmHOaFVZ52j1W1QKYrsFHyT6U/h9bqfHJDxfQ8kz7y9q+W1YPKZ15Ee7yuCA==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.168.20': - resolution: {integrity: sha512-G5S63xDr2AxADtWP+0tQtJsduijT/Mk85hDh3Od4ct8UV0PHr2f/H1CGl72lYWLgcWRMn6bKJoQhz27QFIgLRQ==} + '@tanstack/router-plugin@1.168.35': + resolution: {integrity: sha512-foDAZKFqHXae+oFbIgcsSvy2QCVRn7XdS3nhwcRvD+ed6JrKPUP/1lQMsZLJqWycgR1vkZF7gs955KGa0NZQ0w==} engines: {node: '>=20.19'} peerDependencies: '@rsbuild/core': '>=1.0.2 || ^2.0.0' - '@tanstack/react-router': ^1.170.18 + '@tanstack/react-router': ^1.170.32 vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' vite-plugin-solid: ^2.11.10 || ^3.0.0-0 webpack: '>=5.92.0' @@ -1587,16 +1600,16 @@ packages: resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} engines: {node: '>=20.19'} - '@tanstack/start-client-core@1.170.14': - resolution: {integrity: sha512-yasBgEIFSWysL4EiFIGwp638nCoXXKiTqkc48EP2oty4OyNsZPTC1yfJ82zjq2KGkTAYtIaeMl7otqqRl1n85Q==} + '@tanstack/start-client-core@1.170.27': + resolution: {integrity: sha512-Ro6ZSM0NgYKDMxM0e8qyU4mBfnld2Zb74BA/9f4i35C0Y3IAA8Zxs/DIPfOo9VRYWp5c5n5Q8dRBn2qn0vtPhQ==} engines: {node: '>=22.12.0'} '@tanstack/start-fn-stubs@1.162.0': resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} engines: {node: '>=22.12.0'} - '@tanstack/start-plugin-core@1.171.20': - resolution: {integrity: sha512-s2n82ykGXGkDSoJwGZP85vC9Dv5vAN6fya5TCSm/cvUn+r/g30iDR1x+Ptizas3fIvcracSOOvRnma4RJ8Orww==} + '@tanstack/start-plugin-core@1.171.39': + resolution: {integrity: sha512-Zyj6G4MDFLXcHYhPavhewCBo8dsxi3qvPk31zl/QtTzYzOY9rJHDDBGarKsJq20ziChmkGn/sttYqb4vGCxJDA==} engines: {node: '>=22.12.0'} peerDependencies: '@rsbuild/core': ^2.0.0 @@ -1607,12 +1620,12 @@ packages: vite: optional: true - '@tanstack/start-server-core@1.169.17': - resolution: {integrity: sha512-u0N+PHJhMHnzfnlXYI9F+A/qweDe3E2X0mfkORPGIEkNQgvS548RA9fjwvixR2en5b848CfpEqUzwFhm/tQ40Q==} + '@tanstack/start-server-core@1.169.31': + resolution: {integrity: sha512-56w8l+Fao01YCrmv0hzNxL3b3FRmqLRGdE11izZNQsW+1CcSYuehAM5khWKABuI1DI/UIBLGV7BwL4Dlg0eHCw==} engines: {node: '>=22.12.0'} - '@tanstack/start-storage-context@1.167.17': - resolution: {integrity: sha512-ntkDyGx0PE0opIlWNAMpkMb8qkjR4uyCUOfC0CiT0STM25+EcwPuwYNfDXXeVObMrTAPgsQ4yOj3xdY0Xr4ptw==} + '@tanstack/start-storage-context@1.167.29': + resolution: {integrity: sha512-8qfprC5774XMRDQlMogkfiGpFLiBf0xDG4bMFUbfkSzpCAQwpLbgGY4Zwft22O9rKYq2vUXusKAdVjvJTUEquQ==} engines: {node: '>=22.12.0'} '@tanstack/store@0.9.3': @@ -2129,9 +2142,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@2.0.1: - resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==} - cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} @@ -2261,9 +2271,6 @@ packages: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} - defu@6.1.7: - resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2272,9 +2279,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2484,6 +2488,24 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-runner@0.1.16: + resolution: {integrity: sha512-2LRJM4P2KLX6J83QZZrMqvgCDt/D5ea7wPcI3yYiy5cG/9rX5QwdwZFx0D7ktWnjdRyZxYjttGGorb5nFqb1CA==} + hasBin: true + peerDependencies: + '@netlify/runtime': ^4.1.23 + '@vercel/queue': '>=0.2.0' + miniflare: ^4.20260515.0 + wrangler: ^4.0.0 + peerDependenciesMeta: + '@netlify/runtime': + optional: true + '@vercel/queue': + optional: true + miniflare: + optional: true + wrangler: + optional: true + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -2586,6 +2608,7 @@ packages: eslint@9.39.5: resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -2817,17 +2840,18 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - h3@2.0.1-rc.2: - resolution: {integrity: sha512-2vS7OETzPDzGQxmmcs6ttu7p0NW25zAdkPXYOr43dn4GZf81uUljJvupa158mcpUGpsQUqIy4O4THWUQT1yVeA==} + h3@2.0.1-rc.20: + resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} engines: {node: '>=20.11.1'} + hasBin: true peerDependencies: crossws: ^0.4.1 peerDependenciesMeta: crossws: optional: true - h3@2.0.1-rc.20: - resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} + h3@2.0.1-rc.22: + resolution: {integrity: sha512-Esv0DMIuPkCTSWCA0vO73vcTqwzH1wjSrAO1TXNu/K3up1sZHa9EKMapbmxCDYBeymC3fVTk4qxp7ogQWQ+KgA==} engines: {node: '>=20.11.1'} hasBin: true peerDependencies: @@ -2852,6 +2876,9 @@ packages: resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==} engines: {node: '>=16.9.0'} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -2868,6 +2895,9 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + httpxy@0.5.5: + resolution: {integrity: sha512-uDjmnPyp1q4Sgzf3w+J/Fc6UqcCEj0x4Wjp7OqK5dGhNeDgpyrAmnS6ey8QWrX3SWDon2DMKf9sBa5X9+CVyMA==} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -3271,27 +3301,39 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - nf3@0.1.12: - resolution: {integrity: sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ==} + nf3@0.3.24: + resolution: {integrity: sha512-HxLK4bo+5jNsEETZp4w3tJblHOA9MCBY14IN9nZJJV8JDxt9yNIYxuBuLMjTAR5GFa3HL61+8VQDUrXv3/C8fw==} - nitro-nightly@4.0.0-20251010-091516-7cafddba: - resolution: {integrity: sha512-biADkmoR/Nb9OmUURTfDI2BpGo2IMEt2RbsiMhjqdwz2w3tEm+tx0Hd28LXjBCwid+l8jpqyfmpwc8jzwdng3w==} + nitro@3.0.260610-beta: + resolution: {integrity: sha512-KPb4L5yaF/Rx/xoGMpgHRJvZhbhGiqbRKOwwPLCH9jKTKTsEUHLjnJas85AeCzaswqa8Wi52eQBtRsODC4PS0Q==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - rolldown: '*' - vite: ^7 + '@vercel/queue': ^0.3.0 + dotenv: '*' + giget: '*' + jiti: ^2.7.0 + rollup: ^4.61.1 + vite: ^7 || ^8 xml2js: ^0.6.2 + zephyr-agent: ^0.2.0 peerDependenciesMeta: - rolldown: + '@vercel/queue': + optional: true + dotenv: + optional: true + giget: + optional: true + jiti: + optional: true + rollup: optional: true vite: optional: true xml2js: optional: true - - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + zephyr-agent: + optional: true node-releases@2.0.51: resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} @@ -3321,8 +3363,11 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} - ofetch@1.5.1: - resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + ocache@0.1.5: + resolution: {integrity: sha512-kNNnkkVQup/QDvmTz8Q84wc2ntiyoVHDxa6eHWKt5qdGAmFRBIxy83rxgCYEjW0x06UJ9E3P6VgM2yY4rOBH4w==} + + ofetch@2.0.0-alpha.3: + resolution: {integrity: sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==} ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -3575,10 +3620,6 @@ packages: resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==} engines: {node: '>= 4'} - rendu@0.0.6: - resolution: {integrity: sha512-nZ512Dw0MxKiIYfCVv8DPe6ig4m0Qt3FOYBJEXrammjIYBBPuHaudc0AGfYx+iyOw2q0itAtPywiVZXtTFCsig==} - hasBin: true - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3615,9 +3656,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rou3@0.7.12: - resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} - rou3@0.8.1: resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} @@ -3661,10 +3699,20 @@ packages: peerDependencies: seroval: ^1.0 + seroval-plugins@1.6.2: + resolution: {integrity: sha512-TfxuUjlbBESzUOWdTkTKqvSmav0ABym+itetDXLK6mDz8SmrpdI30aF8RTXE8Bvq+tH/1yIDkvy3W0lfQb1ipQ==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + seroval@1.5.5: resolution: {integrity: sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw==} engines: {node: '>=10'} + seroval@1.6.2: + resolution: {integrity: sha512-mPT+SD2TrlB6wvte1KkYOYUkubaTbd6pZ/6Kk3C9nxzrHmCZyhxOO7XGAeL7f+yLKZglzGtM9odUVvg/EhO+vQ==} + engines: {node: '>=10'} + serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -3742,11 +3790,6 @@ packages: engines: {node: '>=20.16.0'} hasBin: true - srvx@0.8.16: - resolution: {integrity: sha512-hmcGW4CgroeSmzgF1Ihwgl+Ths0JqAJ7HwjP2X7e3JzY7u4IydLMcdnlqGQiQGUswz+PO9oh/KtCpOISIvs9QQ==} - engines: {node: '>=20.16.0'} - hasBin: true - stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} @@ -3924,8 +3967,8 @@ packages: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} - unenv@2.0.0-rc.21: - resolution: {integrity: sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==} + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} @@ -3975,32 +4018,32 @@ packages: unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} - unstorage@2.0.0-alpha.3: - resolution: {integrity: sha512-BeoqISVh8jxqnPseHH7/92twe2VkQztrudXg8RFZVbXb4ckkFdpLk1LnNvsUndDltyodBMVxgI6V7JcbJYt2VQ==} + unstorage@2.0.0-alpha.7: + resolution: {integrity: sha512-ELPztchk2zgFJnakyodVY3vJWGW9jy//keJ32IOJVGUMyaPydwcA1FtVvWqT0TNRch9H+cMNEGllfVFfScImog==} peerDependencies: - '@azure/app-configuration': ^1.8.0 - '@azure/cosmos': ^4.2.0 - '@azure/data-tables': ^13.3.0 - '@azure/identity': ^4.6.0 - '@azure/keyvault-secrets': ^4.9.0 - '@azure/storage-blob': ^12.26.0 - '@capacitor/preferences': ^6.0.3 || ^7.0.0 - '@deno/kv': '>=0.9.0' + '@azure/app-configuration': ^1.11.0 + '@azure/cosmos': ^4.9.1 + '@azure/data-tables': ^13.3.2 + '@azure/identity': ^4.13.0 + '@azure/keyvault-secrets': ^4.10.0 + '@azure/storage-blob': ^12.31.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.13.0' '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 '@planetscale/database': ^1.19.0 - '@upstash/redis': ^1.34.3 - '@vercel/blob': '>=0.27.1' + '@upstash/redis': ^1.36.2 + '@vercel/blob': '>=0.27.3' '@vercel/functions': ^2.2.12 || ^3.0.0 '@vercel/kv': ^1.0.1 aws4fetch: ^1.0.20 - chokidar: ^4.0.3 - db0: '>=0.2.1' - idb-keyval: ^6.2.1 - ioredis: ^5.4.2 - lru-cache: ^11.2.2 - mongodb: ^6.20.0 - ofetch: ^1.4.1 - uploadthing: ^7.4.4 + chokidar: ^4 || ^5 + db0: '>=0.3.4' + idb-keyval: ^6.2.2 + ioredis: ^5.9.3 + lru-cache: ^11.2.6 + mongodb: ^6 || ^7 + ofetch: '*' + uploadthing: ^7.7.4 peerDependenciesMeta: '@azure/app-configuration': optional: true @@ -4324,20 +4367,20 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@7.2.0)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4364,41 +4407,41 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@7.2.0) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@7.2.0) + '@babel/traverse': 7.29.7(supports-color@7.2.0) semver: 6.3.1 transitivePeerDependencies: - supports-color '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.29.7': + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-imports': 7.29.7(supports-color@7.2.0) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -4408,18 +4451,18 @@ snapshots: '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@7.2.0) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@7.2.0)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -4439,43 +4482,43 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@7.2.0) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@7.2.0) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -4487,7 +4530,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 - '@babel/traverse@7.29.7': + '@babel/traverse@7.29.7(supports-color@7.2.0)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 @@ -4495,7 +4538,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color @@ -4766,17 +4809,17 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': dependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@7.2.0)': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -4789,10 +4832,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.6': + '@eslint/eslintrc@3.3.6(supports-color@7.2.0)': dependencies: ajv: 6.15.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -4803,9 +4846,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@9.39.5(jiti@2.7.0))': + '@eslint/js@10.0.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': optionalDependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) '@eslint/js@9.39.5': {} @@ -4880,7 +4923,7 @@ snapshots: dependencies: jsbi: 4.3.2 - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.29.0(supports-color@7.2.0)(zod@3.25.76)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.30) ajv: 8.20.0 @@ -4890,8 +4933,8 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.1.0 - express: 5.2.1 - express-rate-limit: 8.5.2(express@5.2.1) + express: 5.2.1(supports-color@7.2.0) + express-rate-limit: 8.5.2(express@5.2.1(supports-color@7.2.0)) hono: 4.12.30 jose: 6.2.3 json-schema-typed: 8.0.2 @@ -4916,6 +4959,13 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@neodrag/core@3.0.0-next.11': {} + + '@neodrag/solid@3.0.0-next.11(@neodrag/core@3.0.0-next.11)(solid-js@1.9.14)': + dependencies: + '@neodrag/core': 3.0.0-next.11 + solid-js: 1.9.14 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5180,11 +5230,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@9.39.5(jiti@2.7.0))': + '@stylistic/eslint-plugin@5.10.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) '@typescript-eslint/types': 8.64.0 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -5263,11 +5313,26 @@ snapshots: tailwindcss: 4.3.2 vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) + '@tanstack/devtools-bundler-core@0.1.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@tanstack/devtools-client': 0.0.8 + '@tanstack/devtools-event-bus': 0.4.3 + chalk: 5.6.2 + launch-editor: 2.14.1 + magic-string: 0.30.21 + oxc-parser: 0.120.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + picomatch: 4.0.5 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - bufferutil + - utf-8-validate + '@tanstack/devtools-client@0.0.8': dependencies: '@tanstack/devtools-event-client': 0.5.0 - '@tanstack/devtools-event-bus@0.4.2': + '@tanstack/devtools-event-bus@0.4.3': dependencies: ws: 8.21.0 transitivePeerDependencies: @@ -5276,7 +5341,7 @@ snapshots: '@tanstack/devtools-event-client@0.5.0': {} - '@tanstack/devtools-ui@0.6.0(csstype@3.2.3)(solid-js@1.9.14)': + '@tanstack/devtools-ui@0.7.1(csstype@3.2.3)(solid-js@1.9.14)': dependencies: clsx: 2.1.1 dayjs: 1.11.21 @@ -5285,15 +5350,12 @@ snapshots: transitivePeerDependencies: - csstype - '@tanstack/devtools-vite@0.8.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': + '@tanstack/devtools-vite@0.8.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': dependencies: + '@tanstack/devtools-bundler-core': 0.1.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@tanstack/devtools-client': 0.0.8 - '@tanstack/devtools-event-bus': 0.4.2 + '@tanstack/devtools-event-bus': 0.4.3 chalk: 5.6.2 - launch-editor: 2.14.1 - magic-string: 0.30.21 - oxc-parser: 0.120.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - picomatch: 4.0.5 vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) transitivePeerDependencies: - '@emnapi/core' @@ -5301,14 +5363,16 @@ snapshots: - bufferutil - utf-8-validate - '@tanstack/devtools@0.12.5(csstype@3.2.3)(solid-js@1.9.14)': + '@tanstack/devtools@0.14.2(csstype@3.2.3)(solid-js@1.9.14)': dependencies: + '@neodrag/core': 3.0.0-next.11 + '@neodrag/solid': 3.0.0-next.11(@neodrag/core@3.0.0-next.11)(solid-js@1.9.14) '@solid-primitives/event-listener': 2.4.6(solid-js@1.9.14) '@solid-primitives/keyboard': 1.3.7(solid-js@1.9.14) '@solid-primitives/resize-observer': 2.2.0(solid-js@1.9.14) '@tanstack/devtools-client': 0.0.8 - '@tanstack/devtools-event-bus': 0.4.2 - '@tanstack/devtools-ui': 0.6.0(csstype@3.2.3)(solid-js@1.9.14) + '@tanstack/devtools-event-bus': 0.4.3 + '@tanstack/devtools-ui': 0.7.1(csstype@3.2.3)(solid-js@1.9.14) clsx: 2.1.1 goober: 2.1.19(csstype@3.2.3) solid-js: 1.9.14 @@ -5317,31 +5381,31 @@ snapshots: - csstype - utf-8-validate - '@tanstack/eslint-config@0.4.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@tanstack/eslint-config@0.4.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: - '@eslint/js': 10.0.1(eslint@9.39.5(jiti@2.7.0)) - '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.5(jiti@2.7.0)) - eslint: 9.39.5(jiti@2.7.0) - eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-n: 17.24.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@eslint/js': 10.0.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@stylistic/eslint-plugin': 5.10.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) + eslint-plugin-n: 17.24.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(typescript@6.0.3) globals: 17.7.0 - typescript-eslint: 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0)) + typescript-eslint: 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0) transitivePeerDependencies: - '@typescript-eslint/utils' - eslint-import-resolver-node - supports-color - typescript - '@tanstack/history@1.162.0': {} + '@tanstack/history@1.162.1': {} - '@tanstack/query-core@5.101.2': {} + '@tanstack/query-core@5.102.1': {} - '@tanstack/query-devtools@5.101.2': {} + '@tanstack/query-devtools@5.102.1': {} - '@tanstack/react-devtools@0.10.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)': + '@tanstack/react-devtools@0.10.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)': dependencies: - '@tanstack/devtools': 0.12.5(csstype@3.2.3)(solid-js@1.9.14) + '@tanstack/devtools': 0.14.2(csstype@3.2.3)(solid-js@1.9.14) '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) react: 19.2.7 @@ -5352,66 +5416,65 @@ snapshots: - solid-js - utf-8-validate - '@tanstack/react-query-devtools@5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(react@19.2.7)': + '@tanstack/react-query-devtools@5.102.1(@tanstack/react-query@5.102.1(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/query-devtools': 5.101.2 - '@tanstack/react-query': 5.101.2(react@19.2.7) + '@tanstack/query-devtools': 5.102.1 + '@tanstack/react-query': 5.102.1(react@19.2.7) react: 19.2.7 - '@tanstack/react-query@5.101.2(react@19.2.7)': + '@tanstack/react-query@5.102.1(react@19.2.7)': dependencies: - '@tanstack/query-core': 5.101.2 + '@tanstack/query-core': 5.102.1 react: 19.2.7 - '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.15)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tanstack/react-router-devtools@1.167.1(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.27)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.171.15)(csstype@3.2.3) + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-devtools-core': 1.168.1(@tanstack/router-core@1.171.27)(csstype@3.2.3) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@tanstack/router-core': 1.171.15 + '@tanstack/router-core': 1.171.27 transitivePeerDependencies: - csstype - '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.101.2)(@tanstack/react-query@5.101.2(react@19.2.7))(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.15)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tanstack/react-router-ssr-query@1.167.1(@tanstack/query-core@5.102.1)(@tanstack/react-query@5.102.1(react@19.2.7))(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@tanstack/router-core@1.171.27)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/query-core': 5.101.2 - '@tanstack/react-query': 5.101.2(react@19.2.7) - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.101.2)(@tanstack/router-core@1.171.15) + '@tanstack/query-core': 5.102.1 + '@tanstack/react-query': 5.102.1(react@19.2.7) + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-ssr-query-core': 1.169.1(@tanstack/query-core@5.102.1)(@tanstack/router-core@1.171.27) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - '@tanstack/router-core' - '@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/history': 1.162.0 + '@tanstack/history': 1.162.1 '@tanstack/react-store': 0.9.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-core': 1.171.15 + '@tanstack/router-core': 1.171.27 isbot: 5.2.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@tanstack/react-start-client@1.168.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tanstack/react-start-client@1.168.30(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-core': 1.171.15 - '@tanstack/start-client-core': 1.170.14 + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.27 + '@tanstack/start-client-core': 1.170.27 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - '@tanstack/react-start-rsc@0.1.27(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': + '@tanstack/react-start-rsc@0.1.48(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': dependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-core': 1.171.15 - '@tanstack/router-utils': 1.162.2 - '@tanstack/start-client-core': 1.170.14 + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.27 + '@tanstack/router-utils': 1.162.2(supports-color@7.2.0) + '@tanstack/start-client-core': 1.170.27 '@tanstack/start-fn-stubs': 1.162.0 - '@tanstack/start-plugin-core': 1.171.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) - '@tanstack/start-server-core': 1.169.17(crossws@0.4.10(srvx@0.8.16)) - '@tanstack/start-storage-context': 1.167.17 + '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + '@tanstack/start-storage-context': 1.167.29 pathe: 2.0.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -5429,26 +5492,26 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/react-start-server@1.167.22(crossws@0.4.10(srvx@0.8.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@tanstack/react-start-server@1.167.37(crossws@0.4.10(srvx@0.11.22))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-core': 1.171.15 - '@tanstack/start-server-core': 1.169.17(crossws@0.4.10(srvx@0.8.16)) + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-core': 1.171.27 + '@tanstack/start-server-core': 1.169.31(crossws@0.4.10(srvx@0.11.22)) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - crossws - '@tanstack/react-start@1.168.28(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': + '@tanstack/react-start@1.168.49(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': dependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-client': 1.168.16(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/react-start-rsc': 0.1.27(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) - '@tanstack/react-start-server': 1.167.22(crossws@0.4.10(srvx@0.8.16))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@tanstack/router-utils': 1.162.2 - '@tanstack/start-client-core': 1.170.14 - '@tanstack/start-plugin-core': 1.171.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) - '@tanstack/start-server-core': 1.169.17(crossws@0.4.10(srvx@0.8.16)) + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-client': 1.168.30(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-start-rsc': 0.1.48(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + '@tanstack/react-start-server': 1.167.37(crossws@0.4.10(srvx@0.11.22))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/router-utils': 1.162.2(supports-color@7.2.0) + '@tanstack/start-client-core': 1.170.27 + '@tanstack/start-plugin-core': 1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + '@tanstack/start-server-core': 1.169.31(crossws@0.4.10(srvx@0.11.22)) pathe: 2.0.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -5475,34 +5538,34 @@ snapshots: react-dom: 19.2.7(react@19.2.7) use-sync-external-store: 1.6.0(react@19.2.7) - '@tanstack/router-cli@1.167.19': + '@tanstack/router-cli@1.167.33(supports-color@7.2.0)': dependencies: - '@tanstack/router-generator': 1.167.19 + '@tanstack/router-generator': 1.167.33(supports-color@7.2.0) chokidar: 5.0.0 yargs: 17.7.3 transitivePeerDependencies: - supports-color - '@tanstack/router-core@1.171.15': + '@tanstack/router-core@1.171.27': dependencies: - '@tanstack/history': 1.162.0 + '@tanstack/history': 1.162.1 cookie-es: 3.1.1 - seroval: 1.5.5 - seroval-plugins: 1.5.5(seroval@1.5.5) + seroval: 1.6.2 + seroval-plugins: 1.6.2(seroval@1.6.2) - '@tanstack/router-devtools-core@1.168.0(@tanstack/router-core@1.171.15)(csstype@3.2.3)': + '@tanstack/router-devtools-core@1.168.1(@tanstack/router-core@1.171.27)(csstype@3.2.3)': dependencies: - '@tanstack/router-core': 1.171.15 + '@tanstack/router-core': 1.171.27 clsx: 2.1.1 goober: 2.1.19(csstype@3.2.3) optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.167.19': + '@tanstack/router-generator@1.167.33(supports-color@7.2.0)': dependencies: '@babel/types': 7.29.7 - '@tanstack/router-core': 1.171.15 - '@tanstack/router-utils': 1.162.2 + '@tanstack/router-core': 1.171.27 + '@tanstack/router-utils': 1.162.2(supports-color@7.2.0) '@tanstack/virtual-file-routes': 1.162.0 jiti: 2.7.0 magic-string: 0.30.21 @@ -5511,19 +5574,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': + '@tanstack/router-plugin@1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/template': 7.29.7 '@babel/types': 7.29.7 - '@tanstack/router-core': 1.171.15 - '@tanstack/router-generator': 1.167.19 - '@tanstack/router-utils': 1.162.2 + '@tanstack/router-core': 1.171.27 + '@tanstack/router-generator': 1.167.33(supports-color@7.2.0) + '@tanstack/router-utils': 1.162.2(supports-color@7.2.0) chokidar: 5.0.0 unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-router': 1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7) vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) transitivePeerDependencies: - '@farmfe/core' @@ -5535,48 +5598,48 @@ snapshots: - supports-color - unloader - '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.101.2)(@tanstack/router-core@1.171.15)': + '@tanstack/router-ssr-query-core@1.169.1(@tanstack/query-core@5.102.1)(@tanstack/router-core@1.171.27)': dependencies: - '@tanstack/query-core': 5.101.2 - '@tanstack/router-core': 1.171.15 + '@tanstack/query-core': 5.102.1 + '@tanstack/router-core': 1.171.27 - '@tanstack/router-utils@1.162.2': + '@tanstack/router-utils@1.162.2(supports-color@7.2.0)': dependencies: '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/types': 7.29.7 ansis: 4.3.1 - babel-dead-code-elimination: 1.0.12 + babel-dead-code-elimination: 1.0.12(supports-color@7.2.0) diff: 8.0.4 pathe: 2.0.3 tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color - '@tanstack/start-client-core@1.170.14': + '@tanstack/start-client-core@1.170.27': dependencies: - '@tanstack/router-core': 1.171.15 + '@tanstack/router-core': 1.171.27 '@tanstack/start-fn-stubs': 1.162.0 - '@tanstack/start-storage-context': 1.167.17 - seroval: 1.5.5 + '@tanstack/start-storage-context': 1.167.29 + seroval: 1.6.2 '@tanstack/start-fn-stubs@1.162.0': {} - '@tanstack/start-plugin-core@1.171.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.8.16))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': + '@tanstack/start-plugin-core@1.171.39(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1))': dependencies: '@babel/code-frame': 7.27.1 - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 - '@tanstack/router-core': 1.171.15 - '@tanstack/router-generator': 1.167.19 - '@tanstack/router-plugin': 1.168.20(@tanstack/react-router@1.170.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) - '@tanstack/router-utils': 1.162.2 - '@tanstack/start-server-core': 1.169.17(crossws@0.4.10(srvx@0.8.16)) + '@tanstack/router-core': 1.171.27 + '@tanstack/router-generator': 1.167.33(supports-color@7.2.0) + '@tanstack/router-plugin': 1.168.35(@tanstack/react-router@1.170.32(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(supports-color@7.2.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) + '@tanstack/router-utils': 1.162.2(supports-color@7.2.0) + '@tanstack/start-server-core': 1.169.31(crossws@0.4.10(srvx@0.11.22)) exsolve: 1.1.0 lightningcss: 1.32.0 pathe: 2.0.3 picomatch: 4.0.5 - seroval: 1.5.5 + seroval: 1.6.2 source-map: 0.7.6 srvx: 0.11.22 tinyglobby: 0.2.17 @@ -5600,21 +5663,21 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/start-server-core@1.169.17(crossws@0.4.10(srvx@0.8.16))': + '@tanstack/start-server-core@1.169.31(crossws@0.4.10(srvx@0.11.22))': dependencies: - '@tanstack/history': 1.162.0 - '@tanstack/router-core': 1.171.15 - '@tanstack/start-client-core': 1.170.14 - '@tanstack/start-storage-context': 1.167.17 + '@tanstack/history': 1.162.1 + '@tanstack/router-core': 1.171.27 + '@tanstack/start-client-core': 1.170.27 + '@tanstack/start-storage-context': 1.167.29 fetchdts: 0.1.7 - h3-v2: h3@2.0.1-rc.20(crossws@0.4.10(srvx@0.8.16)) - seroval: 1.5.5 + h3-v2: h3@2.0.1-rc.20(crossws@0.4.10(srvx@0.11.22)) + seroval: 1.6.2 transitivePeerDependencies: - crossws - '@tanstack/start-storage-context@1.167.17': + '@tanstack/start-storage-context@1.167.29': dependencies: - '@tanstack/router-core': 1.171.15 + '@tanstack/router-core': 1.171.27 '@tanstack/store@0.9.3': {} @@ -5687,15 +5750,15 @@ snapshots: '@types/validate-npm-package-name@4.0.2': {} - '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.64.0 - '@typescript-eslint/type-utils': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.64.0 - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -5703,23 +5766,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.64.0 '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@7.2.0)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.64.0 - debug: 4.4.3 - eslint: 9.39.5(jiti@2.7.0) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.64.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.64.0(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) '@typescript-eslint/types': 8.64.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5733,13 +5796,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - debug: 4.4.3 - eslint: 9.39.5(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -5747,13 +5810,13 @@ snapshots: '@typescript-eslint/types@8.64.0': {} - '@typescript-eslint/typescript-estree@8.64.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.64.0(supports-color@7.2.0)(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.64.0(typescript@6.0.3) + '@typescript-eslint/project-service': 8.64.0(supports-color@7.2.0)(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) '@typescript-eslint/types': 8.64.0 '@typescript-eslint/visitor-keys': 8.64.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.5 semver: 7.8.5 tinyglobby: 0.2.17 @@ -5762,13 +5825,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) '@typescript-eslint/scope-manager': 8.64.0 '@typescript-eslint/types': 8.64.0 - '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3) - eslint: 9.39.5(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@7.2.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5957,11 +6020,11 @@ snapshots: atomically@1.7.0: {} - babel-dead-code-elimination@1.0.12: + babel-dead-code-elimination@1.0.12(supports-color@7.2.0): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/parser': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@7.2.0) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -5976,11 +6039,11 @@ snapshots: dependencies: require-from-string: 2.0.2 - body-parser@2.3.0: + body-parser@2.3.0(supports-color@7.2.0): dependencies: bytes: 3.1.2 content-type: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) http-errors: 2.0.1 iconv-lite: 0.7.3 on-finished: 2.4.1 @@ -6101,8 +6164,6 @@ snapshots: convert-source-map@2.0.0: {} - cookie-es@2.0.1: {} - cookie-es@3.1.1: {} cookie-signature@1.2.2: {} @@ -6129,9 +6190,9 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crossws@0.4.10(srvx@0.8.16): + crossws@0.4.10(srvx@0.11.22): optionalDependencies: - srvx: 0.8.16 + srvx: 0.11.22 css-tree@3.2.1: dependencies: @@ -6166,9 +6227,11 @@ snapshots: dependencies: mimic-fn: 3.1.0 - debug@4.4.3: + debug@4.4.3(supports-color@7.2.0): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 decimal.js@10.6.0: {} @@ -6189,14 +6252,10 @@ snapshots: define-lazy-prop@3.0.0: {} - defu@6.1.7: {} - depd@2.0.0: {} dequal@2.0.3: {} - destr@2.0.5: {} - detect-libc@2.1.2: {} diff@8.0.4: {} @@ -6258,6 +6317,13 @@ snapshots: env-paths@2.2.1: {} + env-runner@0.1.16: + dependencies: + crossws: 0.4.10(srvx@0.11.22) + exsolve: 1.1.0 + httpxy: 0.5.5 + srvx: 0.11.22 + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -6336,9 +6402,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.39.5(jiti@2.7.0)): + eslint-compat-utils@0.5.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): dependencies: - eslint: 9.39.5(jiti@2.7.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) semver: 7.8.5 eslint-import-context@0.1.9(unrs-resolver@1.12.2): @@ -6348,19 +6414,19 @@ snapshots: optionalDependencies: unrs-resolver: 1.12.2 - eslint-plugin-es-x@7.8.0(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-es-x@7.8.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) '@eslint-community/regexpp': 4.12.2 - eslint: 9.39.5(jiti@2.7.0) - eslint-compat-utils: 0.5.1(eslint@9.39.5(jiti@2.7.0)) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-compat-utils: 0.5.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) - eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)): + eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0): dependencies: '@typescript-eslint/types': 8.64.0 comment-parser: 1.4.7 - debug: 4.4.3 - eslint: 9.39.5(jiti@2.7.0) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-import-context: 0.1.9(unrs-resolver@1.12.2) is-glob: 4.0.3 minimatch: 10.2.5 @@ -6368,16 +6434,16 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.12.2 optionalDependencies: - '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) transitivePeerDependencies: - supports-color - eslint-plugin-n@17.24.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-n@17.24.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(typescript@6.0.3): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) enhanced-resolve: 5.24.2 - eslint: 9.39.5(jiti@2.7.0) - eslint-plugin-es-x: 7.8.0(eslint@9.39.5(jiti@2.7.0)) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-plugin-es-x: 7.8.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 @@ -6405,14 +6471,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.5(jiti@2.7.0): + eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.6 + '@eslint/eslintrc': 3.3.6(supports-color@7.2.0) '@eslint/js': 9.39.5 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -6422,7 +6488,7 @@ snapshots: ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -6513,25 +6579,25 @@ snapshots: expect-type@1.4.0: {} - express-rate-limit@8.5.2(express@5.2.1): + express-rate-limit@8.5.2(express@5.2.1(supports-color@7.2.0)): dependencies: - express: 5.2.1 + express: 5.2.1(supports-color@7.2.0) ip-address: 10.2.0 - express@5.2.1: + express@5.2.1(supports-color@7.2.0): dependencies: accepts: 2.0.0 - body-parser: 2.3.0 + body-parser: 2.3.0(supports-color@7.2.0) content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1 + finalhandler: 2.1.1(supports-color@7.2.0) fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -6542,9 +6608,9 @@ snapshots: proxy-addr: 2.0.7 qs: 6.15.3 range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 + router: 2.2.0(supports-color@7.2.0) + send: 1.2.1(supports-color@7.2.0) + serve-static: 2.2.1(supports-color@7.2.0) statuses: 2.0.2 type-is: 2.1.0 vary: 1.1.2 @@ -6591,9 +6657,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.1: + finalhandler@2.1.1(supports-color@7.2.0): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -6696,21 +6762,19 @@ snapshots: graceful-fs@4.2.11: {} - h3@2.0.1-rc.2(crossws@0.4.10(srvx@0.8.16)): + h3@2.0.1-rc.20(crossws@0.4.10(srvx@0.11.22)): dependencies: - cookie-es: 2.0.1 - fetchdts: 0.1.7 - rou3: 0.7.12 - srvx: 0.8.16 + rou3: 0.8.1 + srvx: 0.11.22 optionalDependencies: - crossws: 0.4.10(srvx@0.8.16) + crossws: 0.4.10(srvx@0.11.22) - h3@2.0.1-rc.20(crossws@0.4.10(srvx@0.8.16)): + h3@2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22)): dependencies: rou3: 0.8.1 srvx: 0.11.22 optionalDependencies: - crossws: 0.4.10(srvx@0.8.16) + crossws: 0.4.10(srvx@0.11.22) has-flag@4.0.0: {} @@ -6722,6 +6786,8 @@ snapshots: hono@4.12.30: {} + hookable@6.1.1: {} + html-encoding-sniffer@6.0.0: dependencies: '@exodus/bytes': 1.15.1 @@ -6736,20 +6802,22 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@7.2.0): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@7.2.0): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) transitivePeerDependencies: - supports-color + httpxy@0.5.5: {} + human-signals@2.1.0: {} human-signals@8.0.1: {} @@ -6845,7 +6913,7 @@ snapshots: jsbi@4.3.2: {} - jsdom@28.1.0: + jsdom@28.1.0(supports-color@7.2.0): dependencies: '@acemir/cssom': 0.9.31 '@asamuzakjp/dom-selector': 6.8.1 @@ -6855,8 +6923,8 @@ snapshots: data-urls: 7.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@7.2.0) + https-proxy-agent: 7.0.6(supports-color@7.2.0) is-potential-custom-element-name: 1.0.1 parse5: 8.0.1 saxes: 6.0.0 @@ -7046,29 +7114,28 @@ snapshots: negotiator@1.0.0: {} - nf3@0.1.12: {} + nf3@0.3.24: {} - nitro-nightly@4.0.0-20251010-091516-7cafddba(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76))(lru-cache@11.5.2)(rolldown@1.1.5)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)): + nitro@3.0.260610-beta(chokidar@5.0.0)(dotenv@17.4.2)(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76))(jiti@2.7.0)(lru-cache@11.5.2)(rollup@4.62.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)): dependencies: consola: 3.4.2 - cookie-es: 2.0.1 - crossws: 0.4.10(srvx@0.8.16) + crossws: 0.4.10(srvx@0.11.22) db0: 0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)) - esbuild: 0.25.12 - fetchdts: 0.1.7 - h3: 2.0.1-rc.2(crossws@0.4.10(srvx@0.8.16)) - jiti: 2.7.0 - nf3: 0.1.12 - ofetch: 1.5.1 + env-runner: 0.1.16 + h3: 2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22)) + hookable: 6.1.1 + nf3: 0.3.24 + ocache: 0.1.5 + ofetch: 2.0.0-alpha.3 ohash: 2.0.11 - rendu: 0.0.6 - rollup: 4.62.2 - srvx: 0.8.16 - undici: 7.28.0 - unenv: 2.0.0-rc.21 - unstorage: 2.0.0-alpha.3(db0@0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)))(lru-cache@11.5.2)(ofetch@1.5.1) - optionalDependencies: rolldown: 1.1.5 + srvx: 0.11.22 + unenv: 2.0.0-rc.24 + unstorage: 2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)))(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3) + optionalDependencies: + dotenv: 17.4.2 + jiti: 2.7.0 + rollup: 4.62.2 vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) transitivePeerDependencies: - '@azure/app-configuration' @@ -7082,6 +7149,7 @@ snapshots: - '@electric-sql/pglite' - '@libsql/client' - '@netlify/blobs' + - '@netlify/runtime' - '@planetscale/database' - '@upstash/redis' - '@vercel/blob' @@ -7094,12 +7162,12 @@ snapshots: - idb-keyval - ioredis - lru-cache + - miniflare - mongodb - mysql2 - sqlite3 - uploadthing - - node-fetch-native@1.6.7: {} + - wrangler node-releases@2.0.51: {} @@ -7120,11 +7188,11 @@ snapshots: obug@2.1.3: {} - ofetch@1.5.1: + ocache@0.1.5: dependencies: - destr: 2.0.5 - node-fetch-native: 1.6.7 - ufo: 1.6.4 + ohash: 2.0.11 + + ofetch@2.0.0-alpha.3: {} ohash@2.0.11: {} @@ -7395,11 +7463,6 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - rendu@0.0.6: - dependencies: - cookie-es: 2.0.1 - srvx: 0.8.16 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -7468,14 +7531,13 @@ snapshots: '@rollup/rollup-win32-x64-gnu': 4.62.2 '@rollup/rollup-win32-x64-msvc': 4.62.2 fsevents: 2.3.3 - - rou3@0.7.12: {} + optional: true rou3@0.8.1: {} - router@2.2.0: + router@2.2.0(supports-color@7.2.0): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -7501,9 +7563,9 @@ snapshots: semver@7.8.5: {} - send@1.2.1: + send@1.2.1(supports-color@7.2.0): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@7.2.0) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -7521,27 +7583,33 @@ snapshots: dependencies: seroval: 1.5.5 + seroval-plugins@1.6.2(seroval@1.6.2): + dependencies: + seroval: 1.6.2 + seroval@1.5.5: {} - serve-static@2.2.1: + seroval@1.6.2: {} + + serve-static@2.2.1(supports-color@7.2.0): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1 + send: 1.2.1(supports-color@7.2.0) transitivePeerDependencies: - supports-color setprototypeof@1.2.0: {} - shadcn@4.13.0(typescript@6.0.3): + shadcn@4.13.0(supports-color@7.2.0)(typescript@6.0.3): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@7.2.0) '@babel/parser': 7.29.7 - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@7.2.0))(supports-color@7.2.0) '@dotenvx/dotenvx': 1.75.1 - '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.29.0(supports-color@7.2.0)(zod@3.25.76) '@types/validate-npm-package-name': 4.0.2 browserslist: 4.28.6 commander: 14.0.3 @@ -7634,8 +7702,6 @@ snapshots: srvx@0.11.22: {} - srvx@0.8.16: {} - stable-hash-x@0.2.0: {} stackback@0.0.2: {} @@ -7767,13 +7833,13 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typescript-eslint@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.5(jiti@2.7.0) + '@typescript-eslint/eslint-plugin': 8.64.0(@typescript-eslint/parser@8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.64.0(supports-color@7.2.0)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -7786,13 +7852,9 @@ snapshots: undici@7.28.0: {} - unenv@2.0.0-rc.21: + unenv@2.0.0-rc.24: dependencies: - defu: 6.1.7 - exsolve: 1.1.0 - ohash: 2.0.11 pathe: 2.0.3 - ufo: 1.6.4 unicorn-magic@0.3.0: {} @@ -7838,11 +7900,12 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - unstorage@2.0.0-alpha.3(db0@0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)))(lru-cache@11.5.2)(ofetch@1.5.1): + unstorage@2.0.0-alpha.7(chokidar@5.0.0)(db0@0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)))(lru-cache@11.5.2)(ofetch@2.0.0-alpha.3): optionalDependencies: + chokidar: 5.0.0 db0: 0.3.4(drizzle-orm@1.0.0-rc.4(@types/pg@8.20.0)(pg@8.22.0)(zod@3.25.76)) lru-cache: 11.5.2 - ofetch: 1.5.1 + ofetch: 2.0.0-alpha.3 update-browserslist-db@1.2.3(browserslist@4.28.6): dependencies: @@ -7882,7 +7945,7 @@ snapshots: optionalDependencies: vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1) - vitest@4.1.10(@types/node@22.20.1)(jsdom@28.1.0)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)): + vitest@4.1.10(@types/node@22.20.1)(jsdom@28.1.0(supports-color@7.2.0))(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.1)) @@ -7906,14 +7969,14 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.20.1 - jsdom: 28.1.0 + jsdom: 28.1.0(supports-color@7.2.0) transitivePeerDependencies: - msw - vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)): + vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0): dependencies: - debug: 4.4.3 - eslint: 9.39.5(jiti@2.7.0) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3984565..6c458ab 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ allowBuilds: - esbuild: false - unrs-resolver: false + esbuild: true + unrs-resolver: true diff --git a/prettier.config.js b/prettier.config.js index aea1c48..f7279f7 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -4,7 +4,7 @@ const config = { semi: false, singleQuote: true, - trailingComma: "all", -}; + trailingComma: 'all', +} -export default config; +export default config diff --git a/public/background.svg b/public/background.svg new file mode 100755 index 0000000..020952f --- /dev/null +++ b/public/background.svg @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/bss-navbar-logo-mobile.svg b/public/bss-navbar-logo-mobile.svg new file mode 100644 index 0000000..732364b --- /dev/null +++ b/public/bss-navbar-logo-mobile.svg @@ -0,0 +1,18 @@ + + + + + + + + Layer 1 + + + + + + + + + + diff --git a/public/bss-navbar-logo.svg b/public/bss-navbar-logo.svg new file mode 100644 index 0000000..84336b8 --- /dev/null +++ b/public/bss-navbar-logo.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/partners/ac.svg b/public/partners/ac.svg new file mode 100755 index 0000000..c5e6e1e --- /dev/null +++ b/public/partners/ac.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/partners/schdesign.svg b/public/partners/schdesign.svg new file mode 100755 index 0000000..743bbd7 --- /dev/null +++ b/public/partners/schdesign.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/partners/schonherz.svg b/public/partners/schonherz.svg new file mode 100755 index 0000000..9f1df31 --- /dev/null +++ b/public/partners/schonherz.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/public/partners/simonyi.svg b/public/partners/simonyi.svg new file mode 100755 index 0000000..931d440 --- /dev/null +++ b/public/partners/simonyi.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/partners/vik.svg b/public/partners/vik.svg new file mode 100755 index 0000000..e36ea49 --- /dev/null +++ b/public/partners/vik.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/social/._facebook.svg b/public/social/._facebook.svg new file mode 100755 index 0000000..357ce8b Binary files /dev/null and b/public/social/._facebook.svg differ diff --git a/public/social/._instagram.svg b/public/social/._instagram.svg new file mode 100755 index 0000000..357ce8b Binary files /dev/null and b/public/social/._instagram.svg differ diff --git a/public/social/._youtube.svg b/public/social/._youtube.svg new file mode 100755 index 0000000..357ce8b Binary files /dev/null and b/public/social/._youtube.svg differ diff --git a/public/social/facebook.svg b/public/social/facebook.svg new file mode 100755 index 0000000..ac0e326 --- /dev/null +++ b/public/social/facebook.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/social/instagram.svg b/public/social/instagram.svg new file mode 100755 index 0000000..988ee29 --- /dev/null +++ b/public/social/instagram.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/social/youtube.svg b/public/social/youtube.svg new file mode 100755 index 0000000..e680649 --- /dev/null +++ b/public/social/youtube.svg @@ -0,0 +1,3 @@ + + + diff --git a/scripts/bootstrap-local.ts b/scripts/bootstrap-local.ts new file mode 100644 index 0000000..046e823 --- /dev/null +++ b/scripts/bootstrap-local.ts @@ -0,0 +1,31 @@ +import { writeLocalFiles } from './lib/local-bootstrap.ts' + +const result = writeLocalFiles(process.cwd()) + +console.log( + 'Lokális infrastruktúra fájlok elkészültek (oob/ könyvtár, gitignore-olt).', +) +if (result.created) { + console.log('Új helyi titkok generálva.') +} else { + console.log('Meglévő helyi titkok megőrizve (idempotens futás).') +} + +console.log('') +console.log('Tesztfelhasználók (jelszavak: oob/local-secrets.json):') +for (const [username, password] of Object.entries(result.secrets.passwords)) { + console.log(` ${username} / ${password}`) +} +console.log('') +console.log('Következő lépések:') +console.log(' 1. docker compose -f docker-compose.dev.yml up -d') +console.log( + ' 2. DATABASE_URL=postgres://bss:bss@127.0.0.1:5582/bss pnpm db:migrate', +) +console.log(' 3. pnpm check:oob') +console.log( + ' Ha az Authentik már fut, a blueprint automatikusan érvényesül; ellenőrzés:', +) +console.log( + ' curl http://127.0.0.1:9000/application/o/bss-stack/.well-known/openid-configuration', +) diff --git a/scripts/check-oob.ts b/scripts/check-oob.ts new file mode 100644 index 0000000..b8dc79d --- /dev/null +++ b/scripts/check-oob.ts @@ -0,0 +1,21 @@ +import { loadOobConfig } from '#/server/config/load.ts' + +const path: string | undefined = + process.argv.length > 2 ? process.argv[2] : undefined + +try { + const config = loadOobConfig(path) + console.log('A BSS OOB config érvényes.') + console.log( + ` Betöltött fájl: ${path ?? process.env['BSS_OOB_CONFIG'] ?? 'oob/config.json'}`, + ) + console.log(` Authentik issuer: ${config.authentik.issuerUrl}`) + console.log( + ` Csoportok: schonherz=${config.authentik.groups.schonherz}, tag=${config.authentik.groups.tag}, vezetoseg=${config.authentik.groups.vezetoseg}`, + ) + console.log(` Méria hostok: ${config.media.allowedHosts.join(', ')}`) + console.log(` Seed fájl helye: ${config.seed.path}`) +} catch (error) { + console.error((error as Error).message) + process.exit(1) +} diff --git a/scripts/import-seed.ts b/scripts/import-seed.ts new file mode 100644 index 0000000..af14fec --- /dev/null +++ b/scripts/import-seed.ts @@ -0,0 +1,76 @@ +import { readFileSync } from 'node:fs' +import 'dotenv/config' +import { drizzle } from 'drizzle-orm/node-postgres' +import { Pool } from 'pg' +import { loadOobConfig } from '#/server/config/load.ts' +import { importSeed } from '#/server/seed/importer.ts' +import { SeedValidationError, validateSeedJson } from '#/server/seed/schema.ts' + +/** + * Seed importer CLI (BSS-034): a OOB config `seed.path` mezőjében rögzített + * JSON-t tölti be idempotensen a DATABASE_URL adatbázisba. + */ +async function main(): Promise { + const databaseUrl = process.env.DATABASE_URL + if (!databaseUrl) { + console.error( + 'A DATABASE_URL környezeti változó nincs beállítva. ' + + 'Állítsd be a .env fájlban (lásd .env.example), majd futtasd újra: pnpm db:seed', + ) + process.exit(1) + } + + const config = loadOobConfig() + const seedPath = config.seed.path + + let rawSeed: unknown + try { + rawSeed = JSON.parse(readFileSync(seedPath, 'utf-8')) + } catch (error) { + console.error( + `Nem olvasható a seed JSON: ${seedPath}. ` + + 'Helyét az OOB config seed.path mezője rögzíti; a tartalmát a scraper állítja elő (docs/oob-inputs.md). ' + + `Eredeti hiba: ${error instanceof Error ? error.message : String(error)}`, + ) + process.exit(1) + } + + let seed + try { + seed = validateSeedJson(rawSeed, config.media) + } catch (error) { + if (error instanceof SeedValidationError) { + console.error(error.message) + process.exit(1) + } + throw error + } + + const pool = new Pool({ connectionString: databaseUrl }) + try { + const db = drizzle({ client: pool }) + const result = await importSeed(db, { seed }) + console.log('A seed betöltése sikeres.') + console.log( + ` Események: ${result.createdEvents} új, ${result.updatedEvents} frissített`, + ) + console.log(` Címkék: ${result.createdTags} új`) + console.log(` Stábszerepek: ${result.createdStaffRoles} új`) + console.log( + ` Videók: ${result.createdVideos} új, ${result.updatedVideos} frissített`, + ) + console.log( + ` Kapcsolatok: ${result.tagLinks} címke-, ${result.staffLinks} stábkapcsolat`, + ) + console.log( + 'Újrafuttatás biztonságos: a változatlan adatokra nem ír semmit.', + ) + } catch (error) { + console.error((error as Error).message) + process.exit(1) + } finally { + await pool.end() + } +} + +void main() diff --git a/scripts/lib/local-bootstrap.ts b/scripts/lib/local-bootstrap.ts new file mode 100644 index 0000000..4a4dbe4 --- /dev/null +++ b/scripts/lib/local-bootstrap.ts @@ -0,0 +1,482 @@ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { randomBytes } from 'node:crypto' +import { join } from 'node:path' +import { validateOobConfig } from '#/server/config/oob-schema.ts' +import type { OobConfig } from '#/server/config/oob-schema.ts' + +export const OOB_DIR = 'oob' +export const SECRETS_FILE = 'local-secrets.json' +export const BLUEPRINT_FILE = join('authentik', 'blueprints', 'bss-local.yaml') +export const AUTHENTIK_ENV_FILE = 'authentik.env' +export const CONFIG_FILE = 'config.json' + +export interface LocalSecrets { + authentikSecretKey: string + oidcClientSecret: string + /** Tagcache-szinkron szolgáltatási fiók Authentik API tokene. */ + syncApiToken: string + passwords: Record +} + +/** + * Publikus alap URL-ek. Lan-eléréshez állítsd pl.: + * BSS_AUTHENTIK_BASE_URL=http://10.128.0.3:9000 + * BSS_APP_BASE_URL=http://10.128.0.3:3000 + */ +const AUTHENTIK_BASE_URL = + process.env.BSS_AUTHENTIK_BASE_URL ?? 'http://127.0.0.1:9000' +const APP_BASE_URL = process.env.BSS_APP_BASE_URL ?? 'http://localhost:3000' +const OIDC_CLIENT_ID = 'bss-stack-local' +export const SYNC_SERVICE_USERNAME = 'svc-bss-sync' + +interface LocalUserSpec { + username: string + fullName: string + nickname: string + groups: string[] + status: string | null + joinedSemester: string | null + introduction: string | null +} + +export const LOCAL_USERS: LocalUserSpec[] = [ + { + username: 'schonherz-dev', + fullName: 'Schönherzes Teszt Felhasználó', + nickname: 'Schi', + groups: ['bss-schonherz'], + status: null, + joinedSemester: null, + introduction: null, + }, + { + username: 'tag-dev', + fullName: 'Teszt BSS Tag', + nickname: 'Tagocska', + groups: ['bss-tag'], + status: 'stúdiós', + joinedSemester: '2023 ősz', + introduction: 'Lokális teszt profil egy stúdiós szerepére.', + }, + { + username: 'vezetoseg-dev', + fullName: 'Teszt Vezetőségi Tag', + nickname: 'Vezér', + groups: ['bss-tag', 'bss-vezetoseg'], + status: 'stúdiós', + joinedSemester: '2021 tavasz', + introduction: 'Lokális teszt profil vezetőségi joggal.', + }, + { + username: 'jelolt-dev', + fullName: 'Teszt Stúdiósjelölt', + nickname: 'Jelöltke', + groups: [], + status: 'stúdiósjelölt', + joinedSemester: '2025 ősz', + introduction: null, + }, + { + username: 'jeloltjelolt-dev', + fullName: 'Teszt Stúdiósjelölt-jelölt', + nickname: 'Jelcsa', + groups: [], + status: 'stúdiósjelölt-jelölt', + joinedSemester: '2026 ősz', + introduction: null, + }, + { + username: 'oregtag-dev', + fullName: 'Teszt Aktív Öregtag', + nickname: 'Öreg', + groups: [], + status: 'aktív öregtag', + joinedSemester: '2019 ősz', + introduction: null, + }, + { + username: 'archivalt-oregtag-dev', + fullName: 'Teszt Archivált Öregtag', + nickname: 'Archie', + groups: [], + status: 'archivált öregtag', + joinedSemester: '2018 ősz', + introduction: null, + }, + { + username: 'kozremukodo-dev', + fullName: 'Teszt Korábbi Közreműködő', + nickname: 'Közreműködő', + groups: [], + status: 'dolgozott még velünk', + joinedSemester: '2020 tavasz', + introduction: null, + }, +] + +function generateToken(): string { + return randomBytes(24).toString('base64url') +} + +export function generateLocalSecrets(): LocalSecrets { + const passwords: Record = {} + for (const user of LOCAL_USERS) { + passwords[user.username] = generateToken() + } + return { + authentikSecretKey: generateToken(), + oidcClientSecret: generateToken(), + syncApiToken: generateToken(), + passwords, + } +} + +export function renderBlueprint(secrets: LocalSecrets): string { + const userEntries = LOCAL_USERS.map((user) => { + const lines: string[] = [] + + if (user.groups.length > 0) { + lines.push(' groups:') + for (const group of user.groups) { + lines.push(` - !KeyOf id-${group}`) + } + } + if ( + user.status !== null || + user.joinedSemester !== null || + user.introduction !== null + ) { + lines.push(' attributes:') + if (user.status !== null) { + lines.push(` bss_status: "${user.status}"`) + } + if (user.joinedSemester !== null) { + lines.push(` bss_csatlakozas: "${user.joinedSemester}"`) + } + if (user.introduction !== null) { + lines.push(' bss_bemutatkozas: >-') + lines.push(` ${user.introduction}`) + } + } + + return [ + ' - model: authentik_core.user', + ' identifiers:', + ` username: ${user.username}`, + ' attrs:', + ` name: ${user.fullName}`, + ` username: ${user.username}`, + ' type: internal', + ` password: "${secrets.passwords[user.username]}"`, + ...lines, + ].join('\n') + }).join('\n') + + return [ + '# AUTOMATIKUSAN GENERÁLT FÁJL - NE SZERKESZD KÉZZEL!', + '# A scripts/bootstrap-local.ts hozza létre az oob/ könyvtárba.', + 'version: 1', + 'entries:', + ' - model: authentik_flows.flow', + ' id: id-authn-flow', + ' identifiers:', + ' slug: bss-local-authentication', + ' attrs:', + ' name: bss-local-authentication', + ' title: Bejelentkezés a BSS Stackbe', + ' designation: authentication', + ' authentication: none', + ' denied_action: message_continue', + ' - model: authentik_flows.flow', + ' id: id-authz-flow', + ' identifiers:', + ' slug: bss-local-authorization', + ' attrs:', + ' name: bss-local-authorization', + ' title: Hozzáférés engedélyezése', + ' designation: authorization', + ' authentication: require_authenticated', + ' denied_action: message_continue', + ' - model: authentik_stages_identification.identificationstage', + ' id: id-stage-identification', + ' identifiers:', + ' name: bss-local-identification', + ' attrs:', + ' name: bss-local-identification', + ' user_fields:', + ' - username', + ' case_insensitive_matching: true', + ' - model: authentik_stages_password.passwordstage', + ' id: id-stage-password', + ' identifiers:', + ' name: bss-local-password', + ' attrs:', + ' name: bss-local-password', + ' backends:', + ' - authentik.core.auth.InbuiltBackend', + ' - model: authentik_stages_user_login.userloginstage', + ' id: id-stage-login', + ' identifiers:', + ' name: bss-local-user-login', + ' attrs:', + ' name: bss-local-user-login', + ' session_duration: days=1', + ' - model: authentik_flows.flowstagebinding', + ' identifiers:', + ' order: 0', + ' target: !KeyOf id-authn-flow', + ' attrs:', + ' order: 0', + ' target: !KeyOf id-authn-flow', + ' stage: !KeyOf id-stage-identification', + ' - model: authentik_flows.flowstagebinding', + ' identifiers:', + ' order: 1', + ' target: !KeyOf id-authn-flow', + ' attrs:', + ' order: 1', + ' target: !KeyOf id-authn-flow', + ' stage: !KeyOf id-stage-password', + ' - model: authentik_flows.flowstagebinding', + ' identifiers:', + ' order: 2', + ' target: !KeyOf id-authn-flow', + ' attrs:', + ' order: 2', + ' target: !KeyOf id-authn-flow', + ' stage: !KeyOf id-stage-login', + ' - model: authentik_core.group', + ' id: id-bss-schonherz', + ' identifiers:', + ' name: bss-schonherz', + ' attrs:', + ' name: bss-schonherz', + ' is_superuser: false', + ' - model: authentik_core.group', + ' id: id-bss-tag', + ' identifiers:', + ' name: bss-tag', + ' attrs:', + ' name: bss-tag', + ' is_superuser: false', + ' - model: authentik_core.group', + ' id: id-bss-vezetoseg', + ' identifiers:', + ' name: bss-vezetoseg', + ' attrs:', + ' name: bss-vezetoseg', + ' is_superuser: false', + userEntries, + ' - model: authentik_core.user', + ' id: id-svc-bss-sync', + ' identifiers:', + ` username: ${SYNC_SERVICE_USERNAME}`, + ' attrs:', + ` name: BSS tagcache szinkron szolgáltatás`, + ` username: ${SYNC_SERVICE_USERNAME}`, + ' type: internal', + ' # Csak a szinkronhoz szükséges olvasási jogok (a régi is_superuser RBAC alatt nem él)', + ' permissions:', + ' - authentik_core.view_user', + ' - authentik_core.view_group', + ' - model: authentik_core.token', + ' identifiers:', + ` identifier: bss-local-sync-token`, + ' attrs:', + ` key: "${secrets.syncApiToken}"`, + ' intent: app_password', + ' expiring: false', + ' user: !KeyOf id-svc-bss-sync', + ' - model: authentik_providers_oauth2.scopemapping', + ' id: id-map-api-scope', + ' identifiers:', + ' name: bss-local-scope-api', + ' attrs:', + ' name: bss-local-scope-api', + ' scope_name: goauthentik.io/api', + ' description: Authentik API hozzáférés a szinkronhoz', + ' expression: |', + ' return {}', + ' - model: authentik_providers_oauth2.scopemapping', + ' id: id-map-profile', + ' identifiers:', + ' name: bss-local-scope-profile', + ' attrs:', + ' name: bss-local-scope-profile', + ' scope_name: profile', + ' description: Profil adatok', + ' expression: |', + ' return {', + ' "preferred_username": request.user.username,', + ' "name": request.user.name,', + ' "nickname": request.user.attributes.get("nickname", request.user.username),', + ' "picture": request.user.avatar,', + ' "groups": [group.name for group in request.user.groups.all()],', + ' }', + ' - model: authentik_providers_oauth2.scopemapping', + ' id: id-map-email', + ' identifiers:', + ' name: bss-local-scope-email', + ' attrs:', + ' name: bss-local-scope-email', + ' scope_name: email', + ' description: Email cím', + ' expression: |', + ' return {"email": request.user.email, "email_verified": True}', + ' - model: authentik_providers_oauth2.scopemapping', + ' id: id-map-bss', + ' identifiers:', + ' name: bss-custom-claims', + ' attrs:', + ' name: bss-custom-claims', + ' scope_name: bss', + ' description: BSS tagsági attribútumok', + ' expression: |', + ' return {', + ' "bss_status": request.user.attributes.get("bss_status", ""),', + ' "bss_csatlakozas": request.user.attributes.get("bss_csatlakozas", ""),', + ' "bss_bemutatkozas": request.user.attributes.get("bss_bemutatkozas", "")', + ' }', + ' - model: authentik_providers_oauth2.oauth2provider', + ' id: bss-stack-provider', + ' identifiers:', + ' name: bss-stack-provider', + ' attrs:', + ' name: bss-stack-provider', + ` client_id: ${OIDC_CLIENT_ID}`, + ` client_secret: "${secrets.oidcClientSecret}"`, + ' redirect_uris:', + ' - matching_mode: strict', + ' url: http://localhost:3000/api/auth/callback', + ' - matching_mode: strict', + ' url: http://127.0.0.1:3000/api/auth/callback', + ...(APP_BASE_URL === 'http://localhost:3000' || + APP_BASE_URL === 'http://127.0.0.1:3000' + ? [] + : [ + ' - matching_mode: strict', + ` url: ${APP_BASE_URL}/api/auth/callback`, + ]), + ' grant_types:', + ' - authorization_code', + ' - refresh_token', + ' - client_credentials', + ' authorization_flow: !KeyOf id-authz-flow', + ' invalidation_flow: !Find [authentik_flows.Flow, [slug, default-provider-invalidation-flow]]', + ' property_mappings:', + ' - !KeyOf id-map-profile', + ' - !KeyOf id-map-email', + ' - !KeyOf id-map-bss', + ' - !KeyOf id-map-api-scope', + ' # user_id sub: a tagcache szinkron az API pk-jával illeszthető', + ' sub_mode: user_id', + ' access_token_validity: hours=1', + ' signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]', + ' - model: authentik_core.application', + ' identifiers:', + ' slug: bss-stack', + ' attrs:', + ' name: BSS Stack', + ' slug: bss-stack', + ' provider: !KeyOf bss-stack-provider', + '', + ].join('\n') +} + +export function renderOobConfig(secrets: LocalSecrets): unknown { + return { + authentik: { + issuerUrl: `${AUTHENTIK_BASE_URL}/application/o/bss-stack/`, + clientId: OIDC_CLIENT_ID, + clientSecret: secrets.oidcClientSecret, + scopes: ['openid', 'profile', 'email', 'bss'], + sync: { + username: SYNC_SERVICE_USERNAME, + token: secrets.syncApiToken, + }, + claims: { + sub: 'sub', + username: 'preferred_username', + fullName: 'name', + nickname: 'nickname', + avatarUrl: 'picture', + }, + groups: { + schonherz: 'bss-schonherz', + tag: 'bss-tag', + vezetoseg: 'bss-vezetoseg', + }, + attributes: { + membershipStatus: { + attribute: 'bss_status', + values: { + stúdiós: 'studio_member', + stúdiósjelölt: 'studio_candidate', + 'stúdiósjelölt-jelölt': 'studio_applicant', + 'aktív öregtag': 'senior_active', + 'archivált öregtag': 'senior_archived', + 'dolgozott még velünk': 'contributor', + }, + }, + joinedSemester: { + attribute: 'bss_csatlakozas', + rules: [ + { pattern: '^(\\d{4})\\s+(ősz|őszi)$', semester: 'autumn' }, + { pattern: '^(\\d{4})\\s+(tavasz|tavaszi)$', semester: 'spring' }, + ], + }, + introduction: 'bss_bemutatkozas', + }, + }, + media: { + allowedHosts: ['v.bsstudio.hu'], + }, + youtube: { + oEmbedEndpoint: 'https://www.youtube.com/oEmbed', + }, + seed: { + path: 'oob/seed.json', + }, + } +} + +export function writeLocalFiles(baseDir: string): { + secrets: LocalSecrets + created: boolean +} { + const oobDir = join(baseDir, OOB_DIR) + mkdirSync(join(oobDir, 'authentik', 'blueprints'), { recursive: true }) + + const secretsPath = join(oobDir, SECRETS_FILE) + let secrets: LocalSecrets + let created = false + + if (existsSync(secretsPath)) { + secrets = JSON.parse(readFileSync(secretsPath, 'utf-8')) as LocalSecrets + // Újabb mezők utólagos kiegészítése régi titokfájlon (idempotens futás). + if (!secrets.syncApiToken) { + secrets.syncApiToken = generateToken() + writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`) + } + } else { + secrets = generateLocalSecrets() + writeFileSync(secretsPath, `${JSON.stringify(secrets, null, 2)}\n`) + created = true + } + + writeFileSync(join(oobDir, BLUEPRINT_FILE), renderBlueprint(secrets)) + writeFileSync( + join(oobDir, AUTHENTIK_ENV_FILE), + `AUTHENTIK_SECRET_KEY=${secrets.authentikSecretKey}\n`, + ) + + const rawConfig = renderOobConfig(secrets) + const validated: OobConfig = validateOobConfig(rawConfig) + writeFileSync( + join(oobDir, CONFIG_FILE), + `${JSON.stringify(rawConfig, null, 2)}\n`, + ) + void validated + + return { secrets, created } +} diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 9079e31..07c50d8 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -1,8 +1,4 @@ -export default function EventCard({ - eventId = 'unknown', -}: Readonly<{ - eventId?: string -}>) { +export default function EventCard() { return (
Kapcsolat
bss@sch.bme.hu -
- {'instagram - {'youtube - {'facebook +
+ {socialLinks.map((social) => ( + + + + ))}
-
- {'ac'} - {'schonherz'} - {'simonyi'} - {'schdesign'} - {'vik'} +
+ {partners.map((partner) => ( + + + + ))}
) diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index e31b91e..1d4a48b 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -1,8 +1,4 @@ - - -export default function MemberCard({ - memberId = 'unknown', -}: Readonly<{ memberId?: string }>) { +export default function MemberCard() { return (
- typeof window !== 'undefined' ? window.location.pathname : '/' - ) + const [menuOpen, setMenuOpen] = useState(false) + const location = useLocation() + const viewerQuery = useQuery({ + queryKey: ['viewer'], + queryFn: fetchViewerState, + staleTime: 60_000, + }) + const viewer = viewerQuery.data + // Close the mobile menu on route change. useEffect(() => { - const handleLocationChange = () => setPathname(window.location.pathname) - - // catch back/forward - window.addEventListener('popstate', handleLocationChange) - - // also patch pushState to detect client-side navigations that don't trigger popstate - const _pushState = history.pushState - ;(history as any).pushState = function (...args: any[]) { - _pushState.call(this, args[0], args[1], args[2]) - handleLocationChange() - } - - return () => { - window.removeEventListener('popstate', handleLocationChange) - // restore - ;(history as any).pushState = _pushState - } - }, []) + setMenuOpen(false) + }, [location.href]) return (
) -} \ No newline at end of file +} + +/** Logs in, returning to the current page; logout lives in the profile menu. */ +function LoginButton() { + const location = useLocation() + const returnTo = `${location.pathname}${location.searchStr}` + return ( + + Belépés + + ) +} diff --git a/src/components/PageStates.tsx b/src/components/PageStates.tsx new file mode 100644 index 0000000..1db225d --- /dev/null +++ b/src/components/PageStates.tsx @@ -0,0 +1,145 @@ +import { Link } from '@tanstack/react-router' + +/** + * Shared page states with Hungarian copy (BSS-019): distinct loading, empty, + * and error states for every list and view (spec 18). + */ + +export function LoadingState({ label = 'Betöltés…' }: { label?: string }) { + return ( +
+

{label}

+
+ ) +} + +export function EmptyState({ + title, + description, +}: { + title: string + description?: string +}) { + return ( +
+

{title}

+ {description !== undefined && ( +

{description}

+ )} +
+ ) +} + +export function ErrorState({ + label = 'Hiba történt az adatok betöltése közben. Próbáld újra később.', +}: { + label?: string +}) { + return ( +
+

Hoppá!

+

+ {label} +

+
+ ) +} + +export function NotFoundContent() { + return ( +
+

404

+

+ Az oldal nem található +

+

+ A keresett tartalom nem létezik, még nem jelent meg, vagy már nem + elérhető. +

+ + Vissza a főoldalra + +
+ ) +} + +export function ForbiddenContent() { + return ( +
+

403

+

+ Hozzáférés megtagadva +

+

+ Ehhez az oldalhoz nincs jogosultságod. +

+ + Vissza a főoldalra + +
+ ) +} + +/* --- Loading placeholders ------------------------------------------------- */ + +/** Placeholder for a line of text; the caller supplies the height. */ +export function SkeletonLine({ className = '' }: { className?: string }) { + return +} + +/** + * Placeholder for a video or event card: 16:9 thumbnail plus a heading. Its + * sizes match the real card's, so the layout doesn't jump when content + * appears. + */ +export function ThumbnailCardSkeleton({ + lines = 1, + className = '', +}: { + lines?: number + className?: string +}) { + return ( +