180 lines
4.2 KiB
TypeScript
180 lines
4.2 KiB
TypeScript
import { wrap } from "@typeschema/valibot";
|
|
import { object, string, minLength, email as emailVal } from "valibot";
|
|
import { TRPCError } from "@trpc/server";
|
|
import {
|
|
createTRPCRouter,
|
|
publicProcedure,
|
|
protectedProcedure,
|
|
} from "../utils";
|
|
import {
|
|
UpdateUserSchema,
|
|
InviteMemberSchema,
|
|
RemoveMemberSchema,
|
|
UpdateRoleSchema,
|
|
} from "../schemas/user";
|
|
import {
|
|
getUserById,
|
|
updateUser,
|
|
deleteUser,
|
|
createUserWithPassword,
|
|
authenticateUser,
|
|
authenticateWithApple,
|
|
refreshAccessToken,
|
|
forgotPassword,
|
|
resetPassword,
|
|
revokeUserSessions,
|
|
} from "~/server/services/user.service";
|
|
import {
|
|
getFamilyGroup,
|
|
inviteMember,
|
|
removeMember,
|
|
updateMemberRole,
|
|
} from "~/server/services/family.service";
|
|
|
|
const LoginSchema = object({
|
|
email: string([emailVal()]),
|
|
password: string([minLength(1)]),
|
|
});
|
|
|
|
const SignupSchema = object({
|
|
name: string([minLength(1)]),
|
|
email: string([emailVal()]),
|
|
password: string([minLength(8)]),
|
|
});
|
|
|
|
const AppleAuthSchema = object({
|
|
identityToken: string([minLength(1)]),
|
|
authorizationCode: string([minLength(1)]),
|
|
userIdentifier: string(),
|
|
});
|
|
|
|
const RefreshTokenSchema = object({
|
|
refreshToken: string([minLength(1)]),
|
|
});
|
|
|
|
const ForgotPasswordSchema = object({
|
|
email: string([emailVal()]),
|
|
});
|
|
|
|
const ResetPasswordSchema = object({
|
|
token: string([minLength(1)]),
|
|
password: string([minLength(8)]),
|
|
});
|
|
|
|
export const userRouter = createTRPCRouter({
|
|
login: publicProcedure
|
|
.input(wrap(LoginSchema))
|
|
.mutation(async ({ input }) => {
|
|
return authenticateUser(input.email, input.password);
|
|
}),
|
|
|
|
signup: publicProcedure
|
|
.input(wrap(SignupSchema))
|
|
.mutation(async ({ input }) => {
|
|
return createUserWithPassword(input.name, input.email, input.password);
|
|
}),
|
|
|
|
appleAuth: publicProcedure
|
|
.input(wrap(AppleAuthSchema))
|
|
.mutation(async ({ input }) => {
|
|
return authenticateWithApple(
|
|
input.identityToken,
|
|
input.authorizationCode,
|
|
input.userIdentifier || null,
|
|
);
|
|
}),
|
|
|
|
refreshToken: publicProcedure
|
|
.input(wrap(RefreshTokenSchema))
|
|
.mutation(async ({ input }) => {
|
|
return refreshAccessToken(input.refreshToken);
|
|
}),
|
|
|
|
forgotPassword: publicProcedure
|
|
.input(wrap(ForgotPasswordSchema))
|
|
.mutation(async ({ input }) => {
|
|
return forgotPassword(input.email);
|
|
}),
|
|
|
|
resetPassword: publicProcedure
|
|
.input(wrap(ResetPasswordSchema))
|
|
.mutation(async ({ input }) => {
|
|
return resetPassword(input.token, input.password);
|
|
}),
|
|
|
|
me: protectedProcedure.query(async ({ ctx }) => {
|
|
const user = await getUserById(ctx.user.id);
|
|
return user;
|
|
}),
|
|
|
|
update: protectedProcedure
|
|
.input(wrap(UpdateUserSchema))
|
|
.mutation(async ({ ctx, input }) => {
|
|
const updated = await updateUser(ctx.user.id, input);
|
|
return updated;
|
|
}),
|
|
|
|
delete: protectedProcedure.mutation(async ({ ctx }) => {
|
|
await deleteUser(ctx.user.id);
|
|
return { success: true };
|
|
}),
|
|
|
|
logout: protectedProcedure.mutation(async ({ ctx }) => {
|
|
await revokeUserSessions(ctx.user.id);
|
|
return { success: true };
|
|
}),
|
|
|
|
listFamilyMembers: protectedProcedure.query(async ({ ctx }) => {
|
|
const group = await getFamilyGroup(ctx.user.id);
|
|
return group.members;
|
|
}),
|
|
|
|
inviteFamilyMember: protectedProcedure
|
|
.input(wrap(InviteMemberSchema))
|
|
.mutation(async ({ ctx, input }) => {
|
|
const group = await getFamilyGroup(ctx.user.id);
|
|
|
|
const callerMember = group.members.find((m) => m.userId === ctx.user.id);
|
|
|
|
if (
|
|
!callerMember ||
|
|
(callerMember.role !== "owner" && callerMember.role !== "admin")
|
|
) {
|
|
throw new TRPCError({
|
|
code: "FORBIDDEN",
|
|
message: "Only owner or admin can invite members",
|
|
});
|
|
}
|
|
|
|
const invitation = await inviteMember(
|
|
group.id,
|
|
input.email,
|
|
ctx.user.id,
|
|
input.role,
|
|
);
|
|
|
|
return invitation;
|
|
}),
|
|
|
|
removeFamilyMember: protectedProcedure
|
|
.input(wrap(RemoveMemberSchema))
|
|
.mutation(async ({ ctx, input }) => {
|
|
const group = await getFamilyGroup(ctx.user.id);
|
|
await removeMember(group.id, input.userId, ctx.user.id);
|
|
return { success: true };
|
|
}),
|
|
|
|
updateFamilyMemberRole: protectedProcedure
|
|
.input(wrap(UpdateRoleSchema))
|
|
.mutation(async ({ ctx, input }) => {
|
|
const group = await getFamilyGroup(ctx.user.id);
|
|
const updated = await updateMemberRole(
|
|
group.id,
|
|
input.userId,
|
|
input.role,
|
|
ctx.user.id,
|
|
);
|
|
return updated;
|
|
}),
|
|
});
|