Skip to content

Commit 30cd273

Browse files
committed
api: Wrap signup in a transaction
1 parent fccf884 commit 30cd273

File tree

2 files changed

+53
-46
lines changed

2 files changed

+53
-46
lines changed

packages/api/services/auth/index.ts

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -106,43 +106,46 @@ export default (io: Server) => {
106106
socket.on("getCsrf", (callback) => callback(""));
107107

108108
socket.on("signup", async (input, callback) => {
109-
const scopedError = await validate(input, [
110-
new UsernameValidation(),
111-
new UsernameCreationValidation(),
112-
new EmailValidation(),
113-
new EmailCreationValidation(),
114-
new PasswordValidation(),
115-
]);
116-
if (scopedError) {
117-
callback({ error: "Bad request", ...scopedError });
118-
} else {
119-
const { username, password, email } = input;
120-
const hashedPassword = getHashedPassword(password);
121-
const user = await prismaDm.user.create({
122-
data: {
123-
username,
124-
password: hashedPassword,
125-
email,
126-
signupDate: new Date(),
127-
},
128-
});
129-
130-
const privileges = (
131-
await prismaDm.userPermission.findMany({
132-
where: {
109+
console.log(`signup with user ${input.username}`);
110+
await prismaDm.$transaction(async (transaction) => {
111+
const scopedError = await validate(transaction, input, [
112+
new UsernameValidation(),
113+
new UsernameCreationValidation(),
114+
new EmailValidation(),
115+
new EmailCreationValidation(),
116+
new PasswordValidation(),
117+
]);
118+
if (scopedError) {
119+
callback({ error: "Bad request", ...scopedError });
120+
} else {
121+
const { username, password, email } = input;
122+
const hashedPassword = getHashedPassword(password);
123+
const user = await transaction.user.create({
124+
data: {
133125
username,
126+
password: hashedPassword,
127+
email,
128+
signupDate: new Date(),
134129
},
135-
})
136-
).groupBy("role", "privilege");
137-
const token = generateAccessToken({
138-
id: user.id,
139-
username,
140-
hashedPassword,
141-
privileges,
142-
});
130+
});
143131

144-
callback(token);
145-
}
132+
const privileges = (
133+
await transaction.userPermission.findMany({
134+
where: {
135+
username,
136+
},
137+
})
138+
).groupBy("role", "privilege");
139+
const token = generateAccessToken({
140+
id: user.id,
141+
username,
142+
hashedPassword,
143+
privileges,
144+
});
145+
146+
callback(token);
147+
}
148+
});
146149
});
147150

148151
socket.on("login", async ({ username, password }, callback) => {

packages/api/services/collection/user/util.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { user } from "~prisma-schemas/schemas/dm";
22
import { prismaClient as prismaDm } from "~prisma-schemas/schemas/dm/client";
33
import { getHashedPassword, isValidEmail } from "~services/auth/util";
44

5+
type PrismaDmTransaction = Parameters<Parameters<typeof prismaDm.$transaction>[0]>[0]
6+
57
interface ScopedError<ErrorKey extends string = string> {
68
error: ErrorKey;
79
message: string;
@@ -13,18 +15,19 @@ export type ScopedErrorDetails = {
1315
selector: ScopedError["selector"];
1416
};
1517

16-
export const getUser = async (id: number) =>
17-
await prismaDm.user.findUniqueOrThrow({
18+
export const getUser = async (id: number, transaction = prismaDm) =>
19+
await transaction.user.findUniqueOrThrow({
1820
omit: { password: true },
1921
where: { id },
2022
});
2123

2224
export const validate = async (
25+
transaction: PrismaDmTransaction,
2326
input: Record<string, unknown>,
2427
validators: Validation[],
2528
): Promise<ScopedErrorDetails | undefined> => {
2629
for (const validator of validators) {
27-
const result = await validator.run(input);
30+
const result = await validator.run(input, transaction);
2831
if (result?.message) {
2932
return result;
3033
}
@@ -34,6 +37,7 @@ export const validate = async (
3437
export abstract class Validation {
3538
abstract run(
3639
data: Record<string, unknown>,
40+
transaction: PrismaDmTransaction,
3741
): Promise<ScopedErrorDetails | undefined>;
3842
}
3943

@@ -49,8 +53,8 @@ export class UsernameValidation extends Validation {
4953
}
5054

5155
export class UsernameCreationValidation extends Validation {
52-
run = async ({ username }: Pick<user, "username">) =>
53-
prismaDm.user
56+
run = async ({ username }: Pick<user, "username">, transaction: PrismaDmTransaction) =>
57+
transaction.user
5458
.findFirstOrThrow({
5559
where: { username },
5660
})
@@ -65,8 +69,8 @@ export class UsernameCreationValidation extends Validation {
6569
}
6670

6771
export class EmailCreationValidation extends Validation {
68-
run = async ({ email }: Pick<user, "email">) =>
69-
prismaDm.user
72+
run = async ({ email }: Pick<user, "email">, transaction: PrismaDmTransaction) =>
73+
transaction.user
7074
.findFirstOrThrow({
7175
where: { email },
7276
})
@@ -81,8 +85,8 @@ export class EmailCreationValidation extends Validation {
8185
}
8286

8387
export class EmailUpdateValidation extends Validation {
84-
run = async ({ id, email }: Pick<user, "email" | "id">) =>
85-
prismaDm.user
88+
run = async ({ id, email }: Pick<user, "email" | "id">, transaction: PrismaDmTransaction) =>
89+
transaction.user
8690
.findUniqueOrThrow({
8791
select: {
8892
email: true,
@@ -92,7 +96,7 @@ export class EmailUpdateValidation extends Validation {
9296
.then(async ({ email: currentEmail }) =>
9397
currentEmail === email
9498
? true
95-
: (await prismaDm.user.count({
99+
: (await transaction.user.count({
96100
where: {
97101
email,
98102
},
@@ -153,8 +157,8 @@ export class OldPasswordValidation extends Validation {
153157
}: {
154158
userId: user["id"];
155159
oldPassword: user["password"];
156-
}) =>
157-
prismaDm.user
160+
}, transaction: PrismaDmTransaction) =>
161+
transaction.user
158162
.findFirstOrThrow({
159163
where: {
160164
id: userId,

0 commit comments

Comments
 (0)