UNPKG

appwrite-server-wrapper

Version:

Wrapper library to handle Appwrite methods including server handling using SSR with NextJS v15 (useActionState, useAction,...)

509 lines (508 loc) 17 kB
"use server"; import { Query } from "node-appwrite"; import { handleApwError } from "../exceptions"; import { getType } from "../collections/typeReader"; import { createAdminClient } from "../appwriteClients"; import { databaseId, usersCollectionId } from "../appwriteConfig"; let ApwUserType; let UserType; const init = async () => { ApwUserType = await getType({ typeFileName: "user", // without extension typeName: "ApwUserType", }); if (!ApwUserType) { throw new Error("No Type 'ApwUserType' found (service: account)."); } UserType = await getType({ typeFileName: "user", // without extension typeName: "UserType", }); if (!UserType) { throw new Error("No Type 'UserType' found (service: account)."); } }; init(); const addPrefsForUserId = async ({ userId, prefs, }) => { try { const { users } = await createAdminClient(); const currentPrefs = await users.getPrefs(userId); // Ensure prefs is a valid JSON string let newPrefs = {}; newPrefs = JSON.parse(prefs); if (typeof newPrefs !== "object" || Array.isArray(newPrefs)) { throw new Error("Invalid prefs format. Must be a stringified JSON object."); } const updatedPrefs = { ...currentPrefs, ...newPrefs }; const user = await users.updatePrefs(userId, updatedPrefs); return { data: user.prefs, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; /* * Update preferences for a user by their ID. */ const updatePrefsForUserId = async ({ userId, prefs, }) => { try { const { users } = await createAdminClient(); const currentPrefs = await users.getPrefs(userId); // Ensure prefs is a valid JSON string let newPrefs = {}; newPrefs = JSON.parse(prefs); if (typeof newPrefs !== "object" || Array.isArray(newPrefs)) { throw new Error("Invalid prefs format. Must be a stringified JSON object."); } const updatedPrefs = { ...currentPrefs, ...newPrefs }; const user = await users.updatePrefs(userId, updatedPrefs); return { data: user.prefs, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const createSessionForUserId = async ({ userId, }) => { try { if (!userId) throw new Error("Invalid param 'userId'"); const { users } = await createAdminClient(); const data = await users.createSession(userId); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const createToken = async ({ userId, length = 32, expire = 60 * 3, }) => { try { const { users } = await createAdminClient(); const data = await users.createToken(userId, length, expire); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const deletePrefsForUserId = async ({ userId, keys, }) => { try { const { users } = await createAdminClient(); const prefs = await users.getPrefs(userId); // Convert keys to an array if it's a stringified JSON let keysToDelete = []; if (typeof keys === "string") { try { const parsedKeys = JSON.parse(keys); keysToDelete = Array.isArray(parsedKeys) ? parsedKeys : [parsedKeys]; } catch { keysToDelete = [keys]; // Treat as a single key if parsing fails } } else { keysToDelete = keys; } // Filter out the keys that need to be removed const newPrefs = Object.fromEntries(Object.entries(prefs).filter(([key]) => !keysToDelete.includes(key))); // Update user preferences const user = await users.updatePrefs(userId, newPrefs); return { data: user.prefs, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const deleteSessionForUserId = async ({ userId, sessionId, }) => { try { const { users } = await createAdminClient(); await users.deleteSession(userId, sessionId); return { data: userId, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const deleteSessionsForUserId = async ({ userId, }) => { try { const { users } = await createAdminClient(); await users.deleteSessions(userId); return { data: userId, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const deleteUserForUserId = async ({ userId, }) => { try { const { users } = await createAdminClient(); const user = await users.get(userId); if (user.status) { throw new Error("Cannot delete user with status active"); } if (user.labels.includes("owner")) { throw new Error("Cannot delete high privilege user"); } await users.delete(userId); return { data: userId, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const getApwUserForUserId = async ({ userId, }) => { try { const { users } = await createAdminClient(); const data = await users.get(userId); if (!data?.$id) { return { data: null, error: null }; } return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const getUserForUserId = async ({ userId, queries, deleted, }) => { try { const { databases } = await createAdminClient(); let queryArray = []; queryArray.push(Query.equal("user_id", userId)); if (deleted !== undefined) { queryArray.push(Query.equal("deleted", deleted)); } let finalQuery; if (queryArray.length > 1) { finalQuery = [Query.and(queryArray)]; } else if (queryArray.length === 1) { finalQuery = queryArray; // Use the single query directly } const { total, documents } = await databases.listDocuments(databaseId, usersCollectionId, finalQuery); return { data: total === 1 ? documents[0] : null, error: null, }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const listUsers = async ({ queries, deleted, }) => { try { const { databases } = await createAdminClient(); let queryArray = queries ? [...queries] : []; if (deleted !== undefined) { queryArray.push(Query.equal("deleted", deleted)); } let finalQuery; if (queryArray.length > 1) { finalQuery = [Query.and(queryArray)]; } else if (queryArray.length === 1) { finalQuery = queryArray; // Use the single query directly } const data = await databases.listDocuments(databaseId, usersCollectionId, finalQuery); return { data, error: null, }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const listApwUsers = async ({ queries, search, blocked, verified, }) => { try { const { users } = await createAdminClient(); let queryArray = queries ? [...queries] : []; if (blocked !== undefined) { const blockedQuery = Query.equal("status", !blocked); queryArray.push(blockedQuery); } if (verified !== undefined) { const verifiedQuery = verified ? Query.or([ Query.equal("emailVerification", true), Query.equal("phoneVerification", true), ]) : Query.and([ Query.equal("emailVerification", false), Query.equal("phoneVerification", false), ]); queryArray.push(verifiedQuery); } const finalQuery = queryArray.length > 0 ? queryArray : undefined; const data = await users.list(finalQuery, search); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const listIdentities = async ({ queries = [], search, }) => { try { const { users } = await createAdminClient(); const data = await users.listIdentities(queries, search); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const listIdentitiesForUserId = async ({ userId, queries = [], search, }) => { try { const { users } = await createAdminClient(); const data = await users.listIdentities(queries.length ? [Query.and([...queries, Query.equal("userId", userId)])] : [Query.equal("userId", userId)], search); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const listSessionsForUserId = async ({ userId, }) => { try { const { users } = await createAdminClient(); const data = await users.listSessions(userId); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updateEmailForUserId = async ({ userId, email, }) => { try { const { users } = await createAdminClient(); const data = await users.updateEmail(userId, email); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updateEmailVerificationForUserId = async ({ userId, emailVerification, }) => { try { const { users } = await createAdminClient(); const data = await users.updateEmailVerification(userId, emailVerification); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updateLabelsForUserId = async ({ userId, labels, }) => { try { const { users } = await createAdminClient(); const newLabels = Array.isArray(labels) ? labels : [String(labels)]; const data = await users.updateLabels(userId, newLabels); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; /* * Adds labels for a user by their ID. */ const addLabelsForUserId = async ({ userId, labels, }) => { try { const { users } = await createAdminClient(); const existingUser = await users.get(userId); const existingLabels = existingUser?.labels || []; let labelsToAdd = []; if (typeof labels === "string" && /^[a-zA-Z0-9]{1,36}$/.test(labels)) { labelsToAdd = [labels]; } else if (Array.isArray(labels)) { if (labels.some((label) => typeof label !== "string")) { throw new Error("Invalid param 'labels': Array items must be strings."); } if (labels.some((label) => !/^[a-zA-Z0-9]{1,36}$/.test(label))) { throw new Error("Invalid param 'labels': Labels must be 1-36 alphanumeric characters."); } labelsToAdd = labels; } else { throw new Error("Invalid param 'labels': Must be a string or string array."); } const newLabels = [...existingLabels]; labelsToAdd.forEach((label) => { if (!newLabels.includes(label)) { newLabels.push(label); } }); const data = await users.updateLabels(userId, newLabels); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; /* * Removes labels for a user by their ID. */ const deleteLabelsForUserId = async ({ userId, labels, }) => { try { const { users } = await createAdminClient(); const existingUser = await users.get(userId); const existingLabels = existingUser?.labels || []; let labelsToRemove = []; if (typeof labels === "string" && /^[a-zA-Z0-9]{1,36}$/.test(labels)) { labelsToRemove = [labels]; } else if (Array.isArray(labels)) { if (labels.some((label) => typeof label !== "string")) { throw new Error("Invalid param 'labels': Array items must be strings."); } if (labels.some((label) => !/^[a-zA-Z0-9]{1,36}$/.test(label))) { throw new Error("Invalid param 'labels': Labels must be 1-36 alphanumeric characters."); } labelsToRemove = labels; } else { throw new Error("Invalid param 'labels': Must be a string or string array."); } const newLabels = existingLabels.filter((label) => !labelsToRemove.includes(label)); const data = await users.updateLabels(userId, newLabels); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updateNameForUserId = async ({ userId, name, }) => { try { const { users } = await createAdminClient(); const data = await users.updateName(userId, name); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updatePasswordForUserId = async ({ userId, password, }) => { try { const { users } = await createAdminClient(); const data = await users.updatePassword(userId, password); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updatePhoneForUserId = async ({ userId, phone, }) => { try { const { users } = await createAdminClient(); const data = await users.updatePhone(userId, phone); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updatePhoneVerificationForUserId = async ({ userId, phoneVerification, }) => { try { const { users } = await createAdminClient(); const data = await users.updatePhoneVerification(userId, phoneVerification); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; const updateStatusForUserId = async ({ userId, status, }) => { try { if (typeof status !== "boolean") { throw new Error("Invalid param 'status'"); } const { users } = await createAdminClient(); const user = await users.get(userId); if (user.labels.includes("owner")) { throw new Error("Cannot update status for high privilege user"); } const data = await users.updateStatus(userId, status); return { data, error: null }; } catch (error) { return { data: null, error: await handleApwError({ error }), }; } }; export { addLabelsForUserId, addPrefsForUserId, createSessionForUserId, createToken, deleteLabelsForUserId, deletePrefsForUserId, deleteSessionForUserId, deleteSessionsForUserId, deleteUserForUserId, getApwUserForUserId, getUserForUserId, // INcl. deleted=false as default listApwUsers, listUsers, // INcl. deleted=false as default listIdentities, listIdentitiesForUserId, listSessionsForUserId, updateEmailForUserId, updateEmailVerificationForUserId, updateLabelsForUserId, updateNameForUserId, updatePasswordForUserId, updatePhoneForUserId, updatePhoneVerificationForUserId, updatePrefsForUserId, updateStatusForUserId, };