lifetracker/packages/trpc/routers/admin.ts
2024-11-14 21:09:59 -08:00

118 lines
3.5 KiB
TypeScript

import { TRPCError } from "@trpc/server";
import { count, eq, sum } from "drizzle-orm";
import { z } from "zod";
import { users } from "@lifetracker/db/schema";
import {
changeRoleSchema,
resetPasswordSchema,
zAdminCreateUserSchema,
} from "@lifetracker/shared/types/admin";
import { hashPassword } from "../auth";
import { adminProcedure, router } from "../index";
import { createUser } from "./users";
export const adminAppRouter = router({
stats: adminProcedure
.output(
z.object({
numUsers: z.number(),
numEntries: z.number(),
}),
)
.query(async ({ ctx }) => {
const [
[{ value: numUsers }],
] = await Promise.all([
ctx.db.select({ value: count() }).from(users)]);
return {
numUsers,
numEntries: 420.69,
};
}),
userStats: adminProcedure
.output(
z.record(
z.string(),
z.object({
numDays: z.number(),
numEntries: z.number(),
}),
),
)
.query(async ({ ctx }) => {
const [userIds] = await Promise.all([
ctx.db.select({ id: users.id }).from(users)
]);
const results: Record<
string,
{ numDays: number; numEntries: number }
> = {};
for (const user of userIds) {
results[user.id] = {
numDays: 0,
numEntries: 0,
};
}
return results;
}),
createUser: adminProcedure
.input(zAdminCreateUserSchema)
.output(
z.object({
id: z.string(),
name: z.string(),
email: z.string(),
role: z.enum(["user", "admin"]).nullable(),
}),
)
.mutation(async ({ input, ctx }) => {
return createUser(input, ctx, input.role);
}),
changeRole: adminProcedure
.input(changeRoleSchema)
.mutation(async ({ input, ctx }) => {
if (ctx.user.id == input.userId) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Cannot change own role",
});
}
const result = await ctx.db
.update(users)
.set({ role: input.role })
.where(eq(users.id, input.userId));
if (!result.changes) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
}),
resetPassword: adminProcedure
.input(resetPasswordSchema)
.mutation(async ({ input, ctx }) => {
if (ctx.user.id == input.userId) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Cannot reset own password",
});
}
const hashedPassword = await hashPassword(input.newPassword);
const result = await ctx.db
.update(users)
.set({ password: hashedPassword })
.where(eq(users.id, input.userId));
if (result.changes == 0) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
}),
});