Files
techtracker/convex/auth.ts

119 lines
3.4 KiB
TypeScript

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 };
},
})