import { ConvexError, v } from 'convex/values'; import { convexAuth, getAuthUserId, retrieveAccount, modifyAccountCredentials, } from '@convex-dev/auth/server'; import { api } from './_generated/api'; import { type Id } from './_generated/dataModel'; import { action, mutation, query } from './_generated/server'; import Password from './CustomPassword'; export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({ providers: [Password], }); export const getUser = query(async (ctx) => { const userId = await getAuthUserId(ctx); if (!userId) return null; const user = await ctx.db.get(userId); if (!user) throw new ConvexError('User not found.'); const image: Id<'_storage'> | null = typeof user.image === 'string' && user.image.length > 0 ? user.image as Id<'_storage'> : null return { id: user._id, email: user.email ?? null, name: user.name ?? null, image, }; }); export const updateUserName = mutation({ args: { name: v.string(), }, handler: async (ctx, { name }) => { const userId = await getAuthUserId(ctx); if (!userId) throw new ConvexError('Not authenticated.'); const user = await ctx.db.get(userId); if (!user) throw new ConvexError('User not found.'); await ctx.db.patch(userId, { name }); return { success: true }; }, }); export const updateUserEmail = mutation({ args: { email: v.string(), }, handler: async (ctx, { email }) => { const userId = await getAuthUserId(ctx); if (!userId) throw new ConvexError('Not authenticated.'); const user = await ctx.db.get(userId); if (!user) throw new ConvexError('User not found.'); await ctx.db.patch(userId, { email }); return { success: true }; } }); export const updateUserImage = mutation({ args: { storageId: v.id('_storage'), }, handler: async (ctx, { storageId }) => { const userId = await getAuthUserId(ctx); if (!userId) throw new ConvexError('Not authenticated.'); const user = await ctx.db.get(userId); if (!user) throw new ConvexError('User not found.'); const oldImage = user.image as Id<'_storage'> | undefined; await ctx.db.patch(userId, { image: storageId }); if (oldImage && oldImage !== storageId) await ctx.storage.delete(oldImage); return { success: true }; }, }); export const validatePassword = (password: string): boolean => { if ( password.length < 8 || password.length > 100 || !/\d/.test(password) || !/[a-z]/.test(password) || !/[A-Z]/.test(password) ) { return false; } return true; }; export const updateUserPassword = action({ args: { currentPassword: v.string(), newPassword: v.string(), }, handler: async (ctx, {currentPassword, newPassword}) => { const userId = await getAuthUserId(ctx); if (!userId) throw new ConvexError('Not authenticated.'); const user = await ctx.runQuery(api.auth.getUser); if (!user?.email) throw new ConvexError('User not found.'); const verified = await retrieveAccount(ctx, { provider: 'password', account: { id: user.email, secret: currentPassword } }); if (!verified) throw new ConvexError('Current password is incorrect.'); if (!validatePassword(newPassword)) throw new ConvexError('Invalid password.'); await modifyAccountCredentials(ctx, { provider: 'password', account: { id: user.email, secret: newPassword } }); return { success: true }; }, })