UNPKG

codalware-auth

Version:

Complete authentication system with enterprise security, attack protection, team workspaces, waitlist, billing, UI components, 2FA, and account recovery - production-ready in 5 minutes. Enhanced CLI with verification, rollback, and App Router scaffolding.

63 lines (58 loc) 4.8 kB
import { Adapter, User, Session, MagicToken } from './types'; import * as sdk from 'node-appwrite'; export function createAppwriteAdapter(opts: { client?: unknown; endpoint?: string; project?: string; apiKey?: string; usersCollectionId?: string; tokensCollectionId?: string; sessionsCollectionId?: string }) : Adapter { const client = (opts.client as any) ?? new (sdk as any).Client().setEndpoint(opts.endpoint || process.env.APPWRITE_ENDPOINT).setProject(opts.project || process.env.APPWRITE_PROJECT).setKey(opts.apiKey || process.env.APPWRITE_KEY); const databases = new sdk.Databases(client); const databaseId = process.env.APPWRITE_DATABASE ?? opts.project ?? 'default'; const usersCollection = opts.usersCollectionId ?? process.env.APPWRITE_USERS_COLLECTION ?? 'users'; const tokensCollection = opts.tokensCollectionId ?? process.env.APPWRITE_TOKENS_COLLECTION ?? 'tokens'; const sessionsCollection = opts.sessionsCollectionId ?? process.env.APPWRITE_SESSIONS_COLLECTION ?? 'sessions'; function mapUser(doc: any): User { return { id: doc.$id, email: doc.email, name: doc.name ?? null, metadata: doc.metadata ?? null, createdAt: new Date(doc.$createdAt), updatedAt: new Date(doc.$updatedAt) }; } return { async createUser({ email, name, metadata }) { const doc = await databases.createDocument(databaseId, usersCollection, sdk.ID.unique(), { email, name, metadata }); return mapUser(doc); }, async getUserById(id) { try { const doc = await databases.getDocument(databaseId, usersCollection, id); return mapUser(doc); } catch { return null; } }, async getUserByEmail(email) { const res = await databases.listDocuments(databaseId, usersCollection, [sdk.Query.equal('email', email)]); const doc = res.documents[0]; if (!doc) return null; return mapUser(doc); }, async updateUser(id, patch) { const data: any = {}; if (patch.email !== undefined) data.email = patch.email; if (patch.name !== undefined) data.name = patch.name; if (patch.metadata !== undefined) data.metadata = patch.metadata; const doc = await databases.updateDocument(databaseId, usersCollection, id, data); return mapUser(doc); }, async createSession(session) { const doc = await databases.createDocument(databaseId, sessionsCollection, sdk.ID.unique(), { userId: session.userId, expiresAt: session.expiresAt.toISOString(), metadata: session.metadata }); return { id: doc.$id, userId: doc.userId, createdAt: new Date(doc.$createdAt), expiresAt: new Date(doc.expiresAt), handle: doc.handle ?? null, metadata: doc.metadata ?? null } as Session; }, async getSessionById(id) { try { const doc = await databases.getDocument(databaseId, sessionsCollection, id); return { id: doc.$id, userId: doc.userId, createdAt: new Date(doc.$createdAt), expiresAt: new Date(doc.expiresAt), handle: doc.handle ?? null, metadata: doc.metadata ?? null } as Session } catch { return null } }, async deleteSession(id) { try { await databases.deleteDocument(databaseId, sessionsCollection, id); } catch { /* ignore */ } }, async deleteSessionsByUserId(userId) { const res = await databases.listDocuments(databaseId, sessionsCollection, [sdk.Query.equal('userId', userId)]); for (const d of res.documents) { await databases.deleteDocument(databaseId, sessionsCollection, d.$id); } }, async storeMagicToken({ tokenHash, userId = null, expiresAt, ip = null, userAgent = null }) { const doc = await databases.createDocument(databaseId, tokensCollection, sdk.ID.unique(), { tokenHash, userId, expiresAt: expiresAt.toISOString(), ip, userAgent }); return { id: doc.$id, tokenHash: doc.tokenHash, userId: doc.userId ?? null, createdAt: new Date(doc.$createdAt), expiresAt: new Date(doc.expiresAt), consumedAt: doc.consumedAt ? new Date(doc.consumedAt) : null, ip: doc.ip ?? null, userAgent: doc.userAgent ?? null } as MagicToken; }, async findValidMagicToken(tokenHash) { const res = await databases.listDocuments(databaseId, tokensCollection, [sdk.Query.equal('tokenHash', tokenHash), sdk.Query.isNull('consumedAt')]); const d = res.documents[0]; if (!d) return null; const expires = new Date(d.expiresAt); if (expires <= new Date()) return null; return { id: d.$id, tokenHash: d.tokenHash, userId: d.userId ?? null, createdAt: new Date(d.$createdAt), expiresAt: new Date(d.expiresAt), consumedAt: d.consumedAt ? new Date(d.consumedAt) : null, ip: d.ip ?? null, userAgent: d.userAgent ?? null } as MagicToken; }, async consumeMagicToken(id) { try { await databases.updateDocument(databaseId, tokensCollection, id, { consumedAt: new Date().toISOString() }); } catch { /* ignore */ } }, }; }