removed sms otp & created normal login & add update user

This commit is contained in:
2024-04-19 19:12:45 +07:00
parent f7392598c5
commit 8e630edfed
2 changed files with 47 additions and 94 deletions

View File

@@ -1,8 +1,4 @@
export const Config = { export const Config = {
sms_api_key: "1796570121771765",
sms_api_secret: "0957b611d575febff1ae0fc51070c8b7",
sms_api_request_endpoint: "https://otp.thaibulksms.com/v2/otp/request",
sms_api_verify_endpoint: "https://otp.thaibulksms.com/v2/otp/verify",
jwt_secret: jwt_secret:
"T4kE6/tIqCVEZYg9lwsqeJjYfOoXTXSXDEMyParsJjj57CjSdkrfPOLWP74/9lJpcBA=", "T4kE6/tIqCVEZYg9lwsqeJjYfOoXTXSXDEMyParsJjj57CjSdkrfPOLWP74/9lJpcBA=",
token_duration: "365d", token_duration: "365d",

View File

@@ -14,8 +14,13 @@ import { TRPCError } from "@trpc/server";
import * as jwt from "jsonwebtoken"; import * as jwt from "jsonwebtoken";
const userInsertSchema = createInsertSchema(user, { const userInsertSchema = createInsertSchema(user, {
cid: (schema) => schema.cid.length(13), cid: (schema) =>
schema.cid.length(13).refine(isValidThaiID, { message: "Invalid Thai ID" }),
}); });
const userUpdateSchema = userInsertSchema
.omit({ id: true, cid: true, phone: true })
.partial();
const opinionInsertSchema = createInsertSchema(userOpinion) const opinionInsertSchema = createInsertSchema(userOpinion)
.omit({ .omit({
userId: true, userId: true,
@@ -30,6 +35,7 @@ const opinionUpdateSchema = createInsertSchema(userOpinion)
.required({ opinionId: true }); .required({ opinionId: true });
type OpinionInsertSchema = z.infer<typeof opinionInsertSchema>; type OpinionInsertSchema = z.infer<typeof opinionInsertSchema>;
type UserInsertSchema = z.infer<typeof userInsertSchema>; type UserInsertSchema = z.infer<typeof userInsertSchema>;
type UserUpdateSchema = z.infer<typeof userUpdateSchema>;
export const userRoute = router({ export const userRoute = router({
getAllUser: publicProcedure getAllUser: publicProcedure
@@ -45,27 +51,21 @@ export const userRoute = router({
async ({ input }) => async ({ input }) =>
await getAllUser(input.offset, input.limit, input.group, input.zone) await getAllUser(input.offset, input.limit, input.group, input.zone)
), ),
createUser: verifiedPhone createUser: publicProcedure
.input( .input(
userInsertSchema.omit({ id: true, phone: true }).extend({ userInsertSchema.omit({ id: true }).extend({
opinions: opinionInsertSchema, opinions: opinionInsertSchema,
}) })
) )
.mutation( .mutation(
async ({ input, ctx }) => async ({ input }) => await createUser({ ...input }, input.opinions)
await createUser({ ...input, phone: ctx.phone }, input.opinions)
), ),
requestOtp: publicProcedure updateUser: protectedProcedure
.input(z.object({ phone: z.string().trim().min(5) })) .input(userUpdateSchema)
.mutation(async ({ input }) => await requestOtp(input.phone)), .mutation(async ({ input, ctx }) => await updateUser(ctx.user.id, input)),
verifyOtp: publicProcedure login: publicProcedure
.input( .input(z.object({ cid: z.string(), phone: z.string() }))
z.object({ .mutation(async ({ input }) => await login(input.cid, input.phone)),
pin: z.string().trim(),
token: z.string(),
})
)
.mutation(async ({ input }) => await verifyOtp(input.token, input.pin)),
changeOpinion: protectedProcedure changeOpinion: protectedProcedure
.input(opinionUpdateSchema) .input(opinionUpdateSchema)
.mutation( .mutation(
@@ -114,95 +114,41 @@ async function createUser(
for (let op of opinions) { for (let op of opinions) {
await db.insert(userOpinion).values({ ...op, userId: result.id }); await db.insert(userOpinion).values({ ...op, userId: result.id });
} }
return { status: "OK" }; return { token: createJWT(newUser.phone) };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
throw new Error(`Unable to create new user:\n${e}`); throw new Error(`Unable to create new user`);
} }
} }
async function requestOtp(phone: string) { async function updateUser(userId: number, update: UserUpdateSchema) {
const _phone = phone.trim();
try { try {
let rs = await fetch(Config.sms_api_request_endpoint, { await db.update(user).set(update).where(eq(user.id, userId));
method: "POST", return { status: "success" };
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
key: Config.sms_api_key,
secret: Config.sms_api_secret,
msisdn: _phone,
}),
}).then((res) => res.json());
if (rs.errors) {
console.error(rs);
throw new TRPCError({
message: `Unable to request OTP`,
code: "INTERNAL_SERVER_ERROR",
});
}
await db.insert(phoneToken).values({ phone: _phone, token: rs.token });
return {
status: rs.status as string,
token: rs.token as string,
refno: rs.refno as string,
};
} catch (e) { } catch (e) {
console.error(e); console.error(e);
throw new TRPCError({ throw new Error(`Unable to update user`);
message: `Unable to request OTP:\n${e}`,
code: "INTERNAL_SERVER_ERROR",
});
} }
} }
async function verifyOtp(token: string, pin: string) { async function login(cid: string, phone: string) {
try { let user = await db.query.user.findFirst({
let pt = await db.query.phoneToken.findFirst({ where: (user, { and, eq }) => and(eq(user.cid, cid), eq(user.phone, phone)),
where: (pt, { eq }) => eq(pt.token, token),
orderBy: (pt, { desc }) => desc(pt.createdOn),
}); });
if (pt === undefined) { if (user === undefined) {
throw new TRPCError({ throw new TRPCError({
message: `Invalid token`, message: "Invalid Credentials",
code: "BAD_REQUEST",
});
}
pt;
let rs = await fetch(Config.sms_api_verify_endpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
key: Config.sms_api_key,
secret: Config.sms_api_secret,
token,
pin,
}),
}).then((res) => res.json());
if (rs.errors) {
console.error(rs);
throw new TRPCError({
message: `Unable to verify OTP`,
code: "BAD_REQUEST", code: "BAD_REQUEST",
}); });
} else { } else {
await db.delete(phoneToken).where(eq(phoneToken.phone, pt.phone)); return { token: createJWT(user.phone) };
const token = jwt.sign({ phone: pt.phone }, Config.jwt_secret, { }
}
function createJWT(phone: string) {
return jwt.sign({ phone: phone }, Config.jwt_secret, {
expiresIn: "365d", expiresIn: "365d",
}); });
return token;
}
} catch (e) {
console.error(e);
throw new TRPCError({
message: `Unable to verify OTP:\n${e}`,
code: "BAD_REQUEST",
});
}
} }
async function changeOpinion( async function changeOpinion(
@@ -229,3 +175,14 @@ async function changeOpinion(
}); });
} }
} }
function isValidThaiID(id: string) {
if (!/^\d{13}$/.test(id)) {
return false;
}
let sum = 0;
for (let i = 0; i < 12; i++) {
sum += Number(id[i]) * (13 - i);
}
const checkSum = (11 - (sum % 11)) % 10;
return checkSum === Number(id[12]);
}