118 lines
3.5 KiB
TypeScript
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",
|
|
});
|
|
}
|
|
}),
|
|
}); |