Adatbázis
Áttekintés
Szekció neve “Áttekintés”Az ElyOS PostgreSQL adatbázist használ Drizzle ORM-mel. A séma a packages/database csomagban él, a repository-k az apps/web/src/lib/server/database/repositories/ mappában.
Séma struktúra
Szekció neve “Séma struktúra”packages/database/src/schemas/├── auth/ # better-auth táblák│ ├── users/ # Felhasználók│ ├── authentication/ # Munkamenetek, tokenek│ ├── groups/ # Csoportok│ ├── roles/ # Szerepkörök│ ├── permissions/ # Jogosultságok│ └── audit/ # Audit napló└── platform/ # Platform táblák ├── apps/ # Alkalmazás regisztráció ├── chat/ # Chat üzenetek ├── desktop/ # Asztali konfiguráció ├── i18n/ # Fordítások ├── logging/ # Rendszernapló ├── notifications/ # Értesítések ├── plugins/ # Plugin metaadatok ├── settings/ # Felhasználói beállítások └── files/ # Fájl metaadatokImportálás
Szekció neve “Importálás”import { db, schema } from '@elyos/database';Drizzle ORM alapok
Szekció neve “Drizzle ORM alapok”Lekérdezés
Szekció neve “Lekérdezés”import { db, schema } from '@elyos/database';import { eq, and, like, desc, asc } from 'drizzle-orm';
// Összes rekordconst users = await db.select().from(schema.users);
// Szűrésselconst activeUsers = await db .select() .from(schema.users) .where(eq(schema.users.isActive, true));
// Több feltételconst result = await db .select() .from(schema.users) .where( and( eq(schema.users.isActive, true), like(schema.users.name, '%admin%') ) ) .orderBy(desc(schema.users.createdAt)) .limit(20) .offset(0);
// Egy rekordconst user = await db .select() .from(schema.users) .where(eq(schema.users.id, userId)) .then(rows => rows[0] ?? null);Beszúrás
Szekció neve “Beszúrás”const [newUser] = await db .insert(schema.users) .values({ name: 'Teszt Felhasználó', email: 'teszt@example.com', isActive: true }) .returning();Frissítés
Szekció neve “Frissítés”const [updated] = await db .update(schema.users) .set({ name: 'Új Név', updatedAt: new Date() }) .where(eq(schema.users.id, userId)) .returning();Törlés
Szekció neve “Törlés”await db .delete(schema.users) .where(eq(schema.users.id, userId));const usersWithGroups = await db .select({ userId: schema.users.id, userName: schema.users.name, groupName: schema.groups.name }) .from(schema.users) .leftJoin( schema.userGroups, eq(schema.users.id, schema.userGroups.userId) ) .leftJoin( schema.groups, eq(schema.userGroups.groupId, schema.groups.id) );Repository minta
Szekció neve “Repository minta”Az adatbázis műveletek repository osztályokban vannak szervezve. Minden repository egy adott entitáshoz tartozó CRUD és üzleti logikát tartalmaz.
import { db, schema } from '@elyos/database';import { eq, and, like, desc, count } from 'drizzle-orm';
export class UserRepository { async findById(id: number) { return db .select() .from(schema.users) .where(eq(schema.users.id, id)) .then(rows => rows[0] ?? null); }
async findManyPaginated(params: { limit: number; offset: number; search?: string; }) { const conditions = [];
if (params.search) { conditions.push(like(schema.users.name, `%${params.search}%`)); }
return db .select() .from(schema.users) .where(conditions.length ? and(...conditions) : undefined) .orderBy(desc(schema.users.createdAt)) .limit(params.limit) .offset(params.offset); }
async countAll(params: { search?: string } = {}) { const conditions = [];
if (params.search) { conditions.push(like(schema.users.name, `%${params.search}%`)); }
const [result] = await db .select({ count: count() }) .from(schema.users) .where(conditions.length ? and(...conditions) : undefined);
return result?.count ?? 0; }}
// Singleton exportexport const userRepository = new UserRepository();Repository importálása
Szekció neve “Repository importálása”import { userRepository } from '$lib/server/database/repositories';Elérhető repository-k
Szekció neve “Elérhető repository-k”| Export | Leírás |
|---|---|
userRepository | Felhasználók kezelése |
groupRepository | Csoportok kezelése |
roleRepository | Szerepkörök kezelése |
permissionRepository | Jogosultságok kezelése |
appRepository | Alkalmazás regisztráció |
notificationRepository | Értesítések |
translationRepository | Fordítások (i18n) |
themePresetsRepository | Téma presetek |
Séma definiálása
Szekció neve “Séma definiálása”Új tábla hozzáadásakor a packages/database/src/schemas/platform/ mappában kell dolgozni:
import { pgTable, serial, text, boolean, timestamp, integer } from 'drizzle-orm/pg-core';
export const items = pgTable('items', { id: serial('id').primaryKey(), name: text('name').notNull(), description: text('description'), isActive: boolean('is_active').notNull().default(true), userId: integer('user_id').notNull(), createdAt: timestamp('created_at').notNull().defaultNow(), updatedAt: timestamp('updated_at').notNull().defaultNow()});Relációk definiálása
Szekció neve “Relációk definiálása”import { relations } from 'drizzle-orm';import { items } from './items/schema';import { users } from '../auth/users/schema';
export const itemsRelations = relations(items, ({ one }) => ({ user: one(users, { fields: [items.userId], references: [users.id] })}));Migrációk
Szekció neve “Migrációk”Sémaváltozás után mindig generálj migrációt:
# Migráció generálásabun db:generate
# Migráció futtatásabun db:migrate
# Vizuális ellenőrzésbun db:studioSeed adatok
Szekció neve “Seed adatok”A seed adatok kezdeti adatokat töltenek be az adatbázisba — alapértelmezett felhasználók, szerepkörök, alkalmazások, fordítások stb. Az ElyOS seed rendszere idempotens (biztonságosan futtatható többször is) és függőség-alapú (automatikus sorrendezés).
Seed struktúra
Szekció neve “Seed struktúra”packages/database/src/seeds/├── config.ts # Seed definíciók és függőségek├── runner.ts # Seed futtatási logika├── init-db.ts # Teljes adatbázis inicializálás├── reset.ts # Teljes adatbázis reset (Docker)├── demo-reset.ts # Demo környezet reset├── sql/ # SQL seed fájlok│ ├── auth/ # Auth séma seed-ek│ │ ├── users.sql│ │ ├── roles.sql│ │ ├── groups.sql│ │ ├── permissions.sql│ │ └── ...│ └── platform/ # Platform séma seed-ek│ ├── apps.sql│ ├── locales.sql│ ├── translations_*.sql│ └── ...└── procedures/ # Stored procedure-ök └── auth/ ├── getGroups.sql └── ...Seed konfigurációs rendszer
Szekció neve “Seed konfigurációs rendszer”A config.ts fájl definiálja az összes seed-et és azok függőségeit:
export const seedConfig: Record<string, SeedDefinition> = { // Nincs függőség roles: { file: 'auth/roles.sql', dependsOn: [], description: 'User roles' },
// Függ a roles seed-től role_permissions: { file: 'auth/role_permissions.sql', dependsOn: ['roles', 'permissions'], description: 'Role-permission assignments' }};A seed runner automatikusan topológiai rendezést végez, így a függőségek mindig a megfelelő sorrendben futnak.
Seed parancsok
Szekció neve “Seed parancsok”# Teljes adatbázis inicializálás (séma + migráció + seed)bun db:init
# Csak seed-ek futtatása (idempotens, nem truncate-el)bun db:seed
# Teljes reset (truncate + seed)bun db:reset
# Csak bizonyos seed-ek futtatása (truncate nélkül)bun db:seed --no-truncate --only=users,roles,apps
# Csak bizonyos procedure-ök futtatásabun db:seed --no-truncate --only-procedures=get_groups,get_groups_2Idempotens seed-ek (upsert logika)
Szekció neve “Idempotens seed-ek (upsert logika)”Minden seed fájl ON CONFLICT DO UPDATE logikát használ, így biztonságosan futtatható többször is:
-- auth/roles.sqlINSERT INTO auth.roles (id, name, description) VALUES (1, '{"hu": "Rendszergazda", "en": "System Administrator"}', '{"hu": "Korlátlan jogosultsággal rendelkező szerep", "en": "Role with unlimited privileges"}'), (2, '{"hu": "Adminisztrátor", "en": "Administrator"}', '{"hu": "Adminisztrációs feladatok elvégzésére jogosult szerep", "en": "Role authorized for administrative tasks"}')ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description;
-- Szekvencia frissítése a legnagyobb id alapjánSELECT setval('auth.roles_id_seq', (SELECT COALESCE(MAX(id), 0) FROM auth.roles));Seed kategóriák
Szekció neve “Seed kategóriák”Auth séma seed-ek
Szekció neve “Auth séma seed-ek”| Seed | Leírás | Függőségek |
|---|---|---|
resources | Erőforrás definíciók (permission system) | - |
providers | Auth provider-ek (email, Google) | - |
groups | Felhasználói csoportok | - |
roles | Szerepkörök (admin, user, stb.) | - |
permissions | Jogosultságok | resources |
role_permissions | Szerepkör-jogosultság hozzárendelések | roles, permissions |
group_permissions | Csoport-jogosultság hozzárendelések | groups, permissions |
users | Kezdeti felhasználók (admin) | - |
accounts | Auth fiókok | users, providers |
user_roles | Felhasználó-szerepkör hozzárendelések | users, roles |
user_groups | Felhasználó-csoport hozzárendelések | users, groups |
role_app_access | Szerepkör-alkalmazás hozzáférés | apps, roles |
group_app_access | Csoport-alkalmazás hozzáférés | apps, groups |
Platform séma seed-ek
Szekció neve “Platform séma seed-ek”| Seed | Leírás | Függőségek |
|---|---|---|
locales | Támogatott nyelvek (hu, en) | - |
translations_common | Közös fordítások (gombok, státuszok) | locales |
translations_settings | Beállítások app fordítások | locales |
translations_log | Napló app fordítások | locales |
translations_desktop | Desktop környezet fordítások | locales |
translations_auth | Auth oldalak fordítások | locales |
translations_user | Felhasználók app fordítások | locales |
translations_notifications | Értesítési rendszer fordítások | locales |
translations_plugin_manager | Plugin Manager fordítások | locales |
apps | Alkalmazás regisztráció (metadata) | - |
email_templates | Email sablonok (HU/EN) | locales |
theme_presets | Téma presetek | locales |
Alapértelmezett admin felhasználó
Szekció neve “Alapértelmezett admin felhasználó”A users.sql seed létrehoz egy rendszergazda felhasználót:
INSERT INTO auth.users (id, full_name, email, email_verified, username, image, user_settings, oauth_image) VALUES (1, 'ElyOS admin', 'youradminemail@eyoursomain.com', true, null, null, '{}', null)ON CONFLICT (id) DO UPDATE SET full_name = EXCLUDED.full_name, email_verified = EXCLUDED.email_verified;Az admin email cím futásidőben felülírható a ADMIN_USER_EMAIL környezeti változóval:
ADMIN_USER_EMAIL=admin@example.comA seed runner automatikusan frissíti az admin user email címét:
async function applyAdminEmail() { const adminEmail = process.env.ADMIN_USER_EMAIL?.trim(); if (!adminEmail) return;
await pool.query( `UPDATE auth.users SET email = $1 WHERE id = (SELECT id FROM auth.users ORDER BY id ASC LIMIT 1)`, [adminEmail] );}Új seed hozzáadása
Szekció neve “Új seed hozzáadása”- Hozd létre az SQL fájlt:
-- packages/database/src/seeds/sql/platform/my_data.sqlINSERT INTO platform.my_table (id, name, value) VALUES (1, 'Item 1', 'Value 1'), (2, 'Item 2', 'Value 2')ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, value = EXCLUDED.value;
SELECT setval('platform.my_table_id_seq', (SELECT COALESCE(MAX(id), 0) FROM platform.my_table));- Regisztráld a
config.tsfájlban:
export const seedConfig: Record<string, SeedDefinition> = { // ... meglévő seed-ek
my_data: { file: 'platform/my_data.sql', dependsOn: ['locales'], // ha van függőség description: 'My custom data' }};- Frissítsd a truncate sorrendet (ha szükséges):
export const truncateOrder = [ // ... meglévő táblák 'platform.my_table', // ...];- Futtasd a seed-et:
bun db:seedStored procedure-ök
Szekció neve “Stored procedure-ök”A seed rendszer támogatja stored procedure-ök létrehozását is:
export const procedureConfig: Record<string, ProcedureDefinition> = { get_groups: { file: 'auth/getGroups.sql', description: 'Get groups by ID' }};-- procedures/auth/getGroups.sqlCREATE OR REPLACE FUNCTION auth.get_groups(group_ids INTEGER[])RETURNS TABLE ( id INTEGER, name JSONB, description JSONB) AS $$BEGIN RETURN QUERY SELECT g.id, g.name, g.description FROM auth.groups g WHERE g.id = ANY(group_ids);END;$$ LANGUAGE plpgsql;Docker inicializálás
Szekció neve “Docker inicializálás”A Docker Compose automatikusan futtatja a seed-eket a db-init konténerben:
db-init: command: > sh -c 'bun --filter @elyos/database db:init ${RESET:+-- --reset}' depends_on: postgres: condition: service_healthyNormál indítás (idempotens):
bun docker:upTeljes reset (truncate + seed):
RESET=1 bun docker:upSeed futtatási folyamat
Szekció neve “Seed futtatási folyamat”A db:init parancs a következő lépéseket hajtja végre:
- PostgreSQL elérhetőség ellenőrzése — health check
- Sémák létrehozása —
auth,platform,extensions - PostgreSQL extensionök engedélyezése —
postgres-json-schema - Drizzle migrációk futtatása — séma alkalmazása
- Seed-ek futtatása — függőségi sorrendben
- Stored procedure-ök létrehozása — ha vannak
- Admin email frissítése — ha
ADMIN_USER_EMAILmeg van adva
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Database Initialization━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🔌 Connecting to PostgreSQL... ✓ PostgreSQL is ready
🏗️ Creating database schemas... ✓ Schema "auth" ready ✓ Schema "platform" ready ✓ Schema "extensions" ready
🔌 Enabling PostgreSQL extensions... ✓ Extension "postgres-json-schema" enabled
📦 Applying database schema... ✓ Migrations completed
🌱 Seeding database... ✓ resources - Resource definitions ✓ providers - Authentication providers ✓ groups - User groups ✓ roles - User roles ✓ permissions - Permissions linked to resources ✓ users - Initial users ✓ locales - Supported locales ✓ apps - Application registry ... (további seed-ek)
⚙️ Creating stored procedures... ✓ get_groups - Get groups by ID
👤 Admin email beállítása: admin@example.com ✓ Admin user email frissítve
🎉 Database initialization completed successfully!Best practice-ek
Szekció neve “Best practice-ek”- Mindig használj upsert logikát —
ON CONFLICT DO UPDATE - Frissítsd a szekvenciákat —
SELECT setval(...) - Definiálj függőségeket — a
dependsOntömbben - Használj leíró neveket —
descriptionmező - Tesztelj idempotenciát — futtasd többször is
- Dokumentáld az adatokat — SQL kommentekkel
- Használj JSONB-t többnyelvű adatokhoz —
{"hu": "...", "en": "..."}
Troubleshooting
Szekció neve “Troubleshooting”Probléma: Seed hiba — duplicate key value violates unique constraint
Megoldás: Ellenőrizd, hogy az ON CONFLICT klauzula megfelelően van-e beállítva.
Probléma: Szekvencia nem frissül — az auto-increment ütközik
Megoldás: Add hozzá a szekvencia frissítést a seed végére:
SELECT setval('schema.table_id_seq', (SELECT COALESCE(MAX(id), 0) FROM schema.table));Probléma: Függőségi hiba — seed nem fut le a megfelelő sorrendben
Megoldás: Ellenőrizd a dependsOn tömböt a config.ts fájlban.
Tranzakciók
Szekció neve “Tranzakciók”await db.transaction(async (tx) => { const [user] = await tx .insert(schema.users) .values({ name: 'Új Felhasználó', email: 'uj@example.com' }) .returning();
await tx .insert(schema.userGroups) .values({ userId: user.id, groupId: defaultGroupId });});Adatbázis-kapcsolat ellenőrzése
Szekció neve “Adatbázis-kapcsolat ellenőrzése”import { ensureDatabaseHealth } from '$lib/server/database/health';
// Ellenőrzi, hogy az adatbázis elérhető-eawait ensureDatabaseHealth();Lapozás (paginálás)
Szekció neve “Lapozás (paginálás)”import { validatePaginationParams } from '$lib/server/utils/database';
const { page, limit, offset } = validatePaginationParams( input.page, // kért oldal (1-től) input.pageSize // oldal méret);