Initial commit for project Spoon!
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import Authentik from '@auth/core/providers/authentik';
|
||||
import {
|
||||
convexAuth,
|
||||
getAuthUserId,
|
||||
modifyAccountCredentials,
|
||||
retrieveAccount,
|
||||
} from '@convex-dev/auth/server';
|
||||
import { ConvexError, v } from 'convex/values';
|
||||
|
||||
import type { Doc, Id } from './_generated/dataModel';
|
||||
import type { QueryCtx } from './_generated/server';
|
||||
import { api } from './_generated/api';
|
||||
import { action, mutation, query } from './_generated/server';
|
||||
import { Password, validatePassword } from './custom/auth';
|
||||
|
||||
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
|
||||
providers: [
|
||||
Authentik({
|
||||
allowDangerousEmailAccountLinking: true,
|
||||
clientId: process.env.AUTH_AUTHENTIK_ID,
|
||||
clientSecret: process.env.AUTH_AUTHENTIK_SECRET,
|
||||
issuer: process.env.AUTH_AUTHENTIK_ISSUER,
|
||||
}),
|
||||
Password,
|
||||
],
|
||||
});
|
||||
|
||||
const getUserById = async (
|
||||
ctx: QueryCtx,
|
||||
userId: Id<'users'>,
|
||||
): Promise<Doc<'users'>> => {
|
||||
const user = await ctx.db.get(userId);
|
||||
if (!user) throw new ConvexError('User not found.');
|
||||
return user;
|
||||
};
|
||||
const getAuthAccountById = async (ctx: QueryCtx, userId: Id<'users'>) => {
|
||||
const user = await ctx.db.get(userId);
|
||||
if (!user) throw new ConvexError('User not found.');
|
||||
const authAccount = await ctx.db
|
||||
.query('authAccounts')
|
||||
.withIndex('userIdAndProvider', (q) => q.eq('userId', userId))
|
||||
.first();
|
||||
if (!authAccount) throw new ConvexError('Auth account not found');
|
||||
return authAccount;
|
||||
};
|
||||
export const getUserProvider = query({
|
||||
args: { userId: v.optional(v.id('users')) },
|
||||
handler: async (ctx, args) => {
|
||||
const userId = args.userId ?? (await getAuthUserId(ctx));
|
||||
if (!userId) return null;
|
||||
const authAccount = await getAuthAccountById(ctx, userId);
|
||||
return authAccount.provider;
|
||||
},
|
||||
});
|
||||
|
||||
export const getUser = query({
|
||||
args: { userId: v.optional(v.id('users')) },
|
||||
handler: async (ctx, args) => {
|
||||
const userId = args.userId ?? (await getAuthUserId(ctx));
|
||||
if (!userId) return null;
|
||||
return getUserById(ctx, userId);
|
||||
},
|
||||
});
|
||||
|
||||
export const updateUser = mutation({
|
||||
args: {
|
||||
name: v.optional(v.string()),
|
||||
email: v.optional(v.string()),
|
||||
image: v.optional(v.id('_storage')),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
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 patch: Partial<{
|
||||
name: string;
|
||||
email: string;
|
||||
image: Id<'_storage'>;
|
||||
}> = {};
|
||||
if (args.name !== undefined) patch.name = args.name;
|
||||
if (args.email !== undefined) patch.email = args.email;
|
||||
if (args.image !== undefined) {
|
||||
const oldImage = user.image as Id<'_storage'> | undefined;
|
||||
patch.image = args.image;
|
||||
if (oldImage && oldImage !== args.image) {
|
||||
await ctx.storage.delete(oldImage);
|
||||
}
|
||||
}
|
||||
if (Object.keys(patch).length > 0) {
|
||||
await ctx.db.patch(userId, patch);
|
||||
}
|
||||
return { success: 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, { userId });
|
||||
if (!user?.email) throw new ConvexError('User not found.');
|
||||
await retrieveAccount(ctx, {
|
||||
provider: 'password',
|
||||
account: { id: user.email, secret: currentPassword },
|
||||
});
|
||||
|
||||
if (!validatePassword(newPassword))
|
||||
throw new ConvexError('Invalid password.');
|
||||
|
||||
await modifyAccountCredentials(ctx, {
|
||||
provider: 'password',
|
||||
account: { id: user.email, secret: newPassword },
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user