UNPKG

lucia-auth-adapter-surrealdb

Version:
199 lines (198 loc) 7.32 kB
import { LuciaError } from "lucia-auth"; import Surreal from "surrealdb.js"; const connect = async (args) => { const opts = args.opts || {}; if (!("uri" in args) || !("ns" in opts) || !("db" in opts) || !("user" in opts) || !("pass" in opts)) { throw "surreal:connection-args:required"; } const surreal = new Surreal(args.uri, { ...args.opts }); await surreal.wait(); return surreal; }; const thing = (target, id) => `${target}:${id}`; const handleId = (target) => `string::replace(id, '${target}:', '') AS id`; const adapter = (args) => { const { user: userTarget = "user", session: sessionTarget = "session", key: keyTarget = "key", } = args.opts?.targets || {}; let surreal; const ensureClient = async () => { if (surreal) return; if ("surreal" in args) { if (args.surreal.status === 1) { throw "surreal:connection:closed"; } surreal = args.surreal; return; } surreal = await connect(args); }; const query = async (sql, vars) => { await ensureClient(); const [response] = await surreal.query(sql, vars); return response && response.status !== "OK" ? [] : response.result; }; const getThing = async (target, id) => { await ensureClient(); const [_thing] = await query(`SELECT *, ${handleId(target)} FROM type::table('${target}') WHERE id = $id`, { id: thing(target, id), }); return _thing || null; }; const deleteThing = async (target, id) => { await ensureClient(); await surreal.delete(thing(target, id)); }; const translateKeyId = (key) => { if (!key) { return null; } const { key_id, ...rest } = key; rest.id = key_id; return rest; }; const getKey = async (keyId) => { await ensureClient(); const [key] = await query(`SELECT *, ${handleId(keyTarget)} FROM ${keyTarget} WHERE key_id = $key_id`, { key_id: keyId, }); return translateKeyId(key); }; const createKey = async (key) => { try { if (await getKey(key.id)) { throw new LuciaError("AUTH_DUPLICATE_KEY_ID"); } const values = { ...key, id: undefined, key_id: key.id, user: `type::thing(${userTarget}, ${key.user_id})`, }; const [created] = await surreal.create(keyTarget, values); return translateKeyId(created); } catch (error) { if (error instanceof Error && error?.message?.includes("already")) { throw new LuciaError("AUTH_DUPLICATE_KEY_ID"); } throw error; } }; return { async getUser(userId) { return getThing(userTarget, userId); }, getSessionAndUserBySessionId: async (sessionId) => { const session = await getThing(sessionTarget, sessionId); if (!session) { return null; } const user = await getThing(userTarget, session.user_id); if (!user) { return null; } return { user, session, }; }, getSession: async (sessionId) => { return getThing(sessionTarget, sessionId); }, getSessionsByUserId: async (userId) => { return query(`SELECT *, ${handleId(sessionTarget)} FROM ${sessionTarget} WHERE user_id = $user_id`, { user_id: userId }); }, setUser: async (userId, userAttributes, key) => { await ensureClient(); if (key && (await getKey(key.id))) { throw new LuciaError("AUTH_DUPLICATE_KEY_ID"); } const [user] = await surreal.create(thing(userTarget, userId), { ...userAttributes, id: userId, }); user.id = user?.id ? user?.id.replace(`${userTarget}:`, "") : user?.id; if (key) { await createKey(key); } return user; }, async deleteUser(userId) { await deleteThing(userTarget, userId); }, setSession: async (session) => { await ensureClient(); const userDoc = await getThing(userTarget, session.user_id); if (!userDoc) throw new LuciaError("AUTH_INVALID_USER_ID"); try { await surreal.create(sessionTarget, { ...session, user: `type::thing(${userTarget}, ${session.user_id})`, }); } catch (error) { if (error instanceof Error && error?.message?.includes("already")) { throw new LuciaError("AUTH_DUPLICATE_SESSION_ID"); } throw error; } }, deleteSession: async (sessionId) => { await deleteThing(sessionTarget, sessionId); }, deleteSessionsByUserId: async (userId) => { await ensureClient(); await surreal.query(`DELETE FROM type::table($tb) WHERE user_id = $user_id`, { tb: sessionTarget, user_id: userId, }); }, updateUserAttributes: async (userId, attributes) => { await ensureClient(); const user = await getThing(userTarget, userId); if (!user) throw new LuciaError("AUTH_INVALID_USER_ID"); await surreal.merge(thing(userTarget, userId), attributes); return (await getThing(userTarget, userId)) || void 0; }, getKey: async (keyId) => { return getKey(keyId); }, setKey: async (key) => { const user = await getThing(userTarget, key.user_id); if (!user) throw new LuciaError("AUTH_INVALID_USER_ID"); await createKey(key); }, getKeysByUserId: async (userId) => { return query(`SELECT *, ${handleId(keyTarget)} FROM ${keyTarget} WHERE user_id = $user_id`, { user_id: userId }).then((keys) => keys.map((k) => translateKeyId(k))); }, updateKeyPassword: async (key, hashedPassword) => { if (!(await getKey(key))) { throw new LuciaError("AUTH_INVALID_KEY_ID"); } await surreal.query(`UPDATE ${keyTarget} SET hashed_password = '${hashedPassword}' WHERE key_id = $key_id`, { key_id: key, }); return (await getKey(key)) || void 0; }, deleteKeysByUserId: async (userId) => { ensureClient(); await surreal.query(`DELETE FROM ${keyTarget} WHERE user_id = $user_id`, { user_id: userId, }); }, deleteNonPrimaryKey: async (keyId) => { ensureClient(); await surreal.query(`DELETE FROM ${keyTarget} WHERE key_id = $key_id AND primary_key = false`, { key_id: keyId, }); }, }; }; export default adapter;