Tovább a tartalomhoz

Szerver függvények

Az alkalmazás szerver oldali logikája a server/functions.js (vagy .ts) fájlban él. Ezek a függvények a szerveren futnak, és a kliensről a sdk.remote.call() segítségével hívhatók.

Szükséges jogosultság: remote_functions a manifest.json-ban.

server/functions.js
/**
* Szerver idő lekérdezése
* @param {Object} params - Kliens által küldött paraméterek
* @param {Object} context - Execution context (pluginId, userId, db)
*/
export async function getServerTime(params, context) {
const now = new Date();
return {
iso: now.toISOString(),
locale: now.toLocaleString('hu-HU'),
timestamp: now.getTime()
};
}

TypeScript-tel:

server/functions.ts
interface Context {
pluginId: string;
userId: string;
db: {
query: (sql: string, params?: unknown[]) => Promise<{ rows: unknown[] }>;
connect: () => Promise<{
query: (sql: string, params?: unknown[]) => Promise<{ rows: unknown[] }>;
release: () => void;
}>;
};
permissions: string[];
pluginPermissions: string[];
email?: { send: (params: unknown) => Promise<{ success: boolean; error?: string }> };
}
export async function getServerTime(
params: { format?: 'ISO' | 'locale' | 'timestamp' },
context: Context
) {
const now = new Date();
return {
iso: now.toISOString(),
locale: now.toLocaleString('hu-HU'),
timestamp: now.getTime()
};
}

Minden szerver függvény megkapja a context paramétert:

MezőTípusLeírás
pluginIdstringA plugin azonosítója
userIdstringA hívó felhasználó ID-ja (numerikus string, pl. "12")
dbobjectpg Pool kompatibilis kapcsolat: query(sql, params) és connect() tranzakciókhoz
permissionsstring[]A hívó felhasználó core jogosultságai (pl. plugin.manual.install). Rendszergazda esetén tartalmazza az admin értéket is.
pluginPermissionsstring[]A plugin manifest.json-ban megadott jogosultságai (pl. database, remote_functions)
emailobject | undefinedEmail szolgáltatás (csak notifications jogosultsággal) — lásd Email szolgáltatás
filesobject | undefinedFájltárolás (csak file_access jogosultsággal) — lásd Fájltárolás
export async function myFunction(params, context) {
const { pluginId, userId, db } = context;
console.log(`[${pluginId}] Called by user: ${userId}`);
// ...
}

A db objektum a core PostgreSQL kapcsolat-poolját adja. A plugin saját táblái az app__{plugin_id} sémában vannak (a kötőjelek aláhúzásra cserélve, pl. my-app → app__my_app). A séma csak database jogosultsággal jön létre telepítéskor.

export async function getItems(params, context) {
const { db, pluginId } = context;
// A plugin saját sémájában lévő tábla lekérdezése
const result = await db.query(`
SELECT id, name, created_at
FROM plugin_${pluginId}.items
WHERE active = $1
ORDER BY created_at DESC
LIMIT $2
`, [true, params.limit ?? 20]);
return {
items: result.rows,
total: result.rows.length
};
}

Tranzakcióhoz a connect()-tel kérj dedikált klienst, és minden esetben engedd el:

export async function transferItems(params, context) {
const client = await context.db.connect();
try {
await client.query('BEGIN');
await client.query(`UPDATE app__${context.pluginId.replace(/-/g, '_')}.items SET owner = $1 WHERE id = $2`, [params.to, params.id]);
await client.query('COMMIT');
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
}
server/functions.js
export async function createItem(params, context) {
const { db, pluginId, userId } = context;
const { name, description } = params;
if (!name || name.trim().length === 0) {
throw new Error('A név megadása kötelező');
}
const result = await db.query(`
INSERT INTO plugin_${pluginId}.items (name, description, created_by)
VALUES ($1, $2, $3)
RETURNING id, name, created_at
`, [name.trim(), description ?? null, userId]);
return { item: result.rows[0] };
}
export async function updateItem(params, context) {
const { db, pluginId } = context;
const { id, name, description } = params;
await db.query(`
UPDATE plugin_${pluginId}.items
SET name = $1, description = $2, updated_at = NOW()
WHERE id = $3
`, [name, description, id]);
return { success: true };
}
export async function deleteItem(params, context) {
const { db, pluginId } = context;
await db.query(`
DELETE FROM plugin_${pluginId}.items WHERE id = $1
`, [params.id]);
return { success: true };
}
<script lang="ts">
const sdk = window.webOS!;
interface Item {
id: number;
name: string;
created_at: string;
}
let items = $state<Item[]>([]);
let loading = $state(false);
async function loadItems() {
loading = true;
try {
const result = await sdk.remote.call<{ items: Item[] }>('getItems', {
limit: 50
});
items = result.items;
} catch (error) {
sdk.ui.toast('Nem sikerült betölteni az elemeket', 'error');
} finally {
loading = false;
}
}
async function addItem(name: string) {
try {
await sdk.remote.call('createItem', { name });
sdk.ui.toast('Elem létrehozva', 'success');
await loadItems();
} catch (error) {
sdk.ui.toast((error as Error).message, 'error');
}
}
</script>

A szerver függvényekből dobott hibák automatikusan propagálódnak a kliensre:

export async function riskyOperation(params, context) {
if (!params.id) {
throw new Error('Az ID megadása kötelező');
}
try {
const result = await context.db.query(
`SELECT * FROM plugin_${context.pluginId}.items WHERE id = $1`,
[params.id]
);
if (result.rows.length === 0) {
throw new Error('Az elem nem található');
}
return { item: result.rows[0] };
} catch (error) {
// Naplózás szerver oldalon
console.error(`[${context.pluginId}] Error in riskyOperation:`, error);
// Hiba továbbítása a kliensnek
throw error;
}
}

A kliensen:

try {
const result = await sdk.remote.call('riskyOperation', { id: 123 });
} catch (error) {
// error.message tartalmazza a szerver által dobott hibaüzenetet
sdk.ui.toast(error.message, 'error');
}

A remote hívásoknak alapértelmezetten 30 másodperces timeoutjuk van. Hosszabb műveleteknél adj meg egyedi timeoutot:

const result = await sdk.remote.call('longRunningTask', params, {
timeout: 120000 // 2 perc
});

A Mock SDK-val szimulálhatod a szerver függvényeket fejlesztés közben:

src/main.ts
MockWebOSSDK.initialize({
remote: {
handlers: {
getItems: async () => ({
items: [
{ id: 1, name: 'Teszt elem', created_at: new Date().toISOString() }
]
}),
createItem: async ({ name }) => ({
item: { id: Date.now(), name, created_at: new Date().toISOString() }
})
}
}
});