170 lines
No EOL
4.6 KiB
TypeScript
170 lines
No EOL
4.6 KiB
TypeScript
import { router, protectedProcedure, publicProcedure } from '../trpc-index';
|
|
import jwt from 'jsonwebtoken';
|
|
import { eq, and } from 'drizzle-orm';
|
|
import { z } from 'zod';
|
|
import { db } from '../../db/db_index';
|
|
import { users, userDetails, userCreds, notifCreds, unloggedUserTokens } from '../../db/schema';
|
|
import { ApiError } from '../../lib/api-error';
|
|
import { jwtSecret } from 'src/lib/env-exporter';
|
|
import { generateSignedUrlFromS3Url } from '../../lib/s3-client';
|
|
|
|
interface AuthResponse {
|
|
token: string;
|
|
user: {
|
|
id: number;
|
|
name: string | null;
|
|
email: string | null;
|
|
mobile: string | null;
|
|
profileImage?: string | null;
|
|
bio?: string | null;
|
|
dateOfBirth?: string | null;
|
|
gender?: string | null;
|
|
occupation?: string | null;
|
|
};
|
|
}
|
|
|
|
const generateToken = (userId: number): string => {
|
|
const secret = jwtSecret;
|
|
if (!secret) {
|
|
throw new ApiError('JWT secret not configured', 500);
|
|
}
|
|
|
|
return jwt.sign({ userId }, secret, { expiresIn: '7d' });
|
|
};
|
|
|
|
export const userRouter = router({
|
|
getSelfData: protectedProcedure
|
|
.query(async ({ ctx }) => {
|
|
const userId = ctx.user.userId;
|
|
|
|
if (!userId) {
|
|
throw new ApiError('User not authenticated', 401);
|
|
}
|
|
|
|
const [user] = await db
|
|
.select()
|
|
.from(users)
|
|
.where(eq(users.id, userId))
|
|
.limit(1);
|
|
|
|
if (!user) {
|
|
throw new ApiError('User not found', 404);
|
|
}
|
|
|
|
// Get user details for profile image
|
|
const [userDetail] = await db
|
|
.select()
|
|
.from(userDetails)
|
|
.where(eq(userDetails.userId, userId))
|
|
.limit(1);
|
|
|
|
// Generate signed URL for profile image if it exists
|
|
const profileImageSignedUrl = userDetail?.profileImage
|
|
? await generateSignedUrlFromS3Url(userDetail.profileImage)
|
|
: null;
|
|
|
|
const response: Omit<AuthResponse, 'token'> = {
|
|
user: {
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
mobile: user.mobile,
|
|
profileImage: profileImageSignedUrl,
|
|
bio: userDetail?.bio || null,
|
|
dateOfBirth: userDetail?.dateOfBirth || null,
|
|
gender: userDetail?.gender || null,
|
|
occupation: userDetail?.occupation || null,
|
|
},
|
|
};
|
|
|
|
return {
|
|
success: true,
|
|
data: response,
|
|
};
|
|
}),
|
|
|
|
checkProfileComplete: protectedProcedure
|
|
.query(async ({ ctx }) => {
|
|
const userId = ctx.user.userId;
|
|
|
|
if (!userId) {
|
|
throw new ApiError('User not authenticated', 401);
|
|
}
|
|
|
|
const result = await db
|
|
.select()
|
|
.from(users)
|
|
.leftJoin(userCreds, eq(users.id, userCreds.userId))
|
|
.where(eq(users.id, userId))
|
|
.limit(1);
|
|
|
|
if (result.length === 0) {
|
|
throw new ApiError('User not found', 404);
|
|
}
|
|
|
|
const { users: user, user_creds: creds } = result[0];
|
|
|
|
return {
|
|
isComplete: !!(user.name && user.email && creds),
|
|
};
|
|
}),
|
|
|
|
savePushToken: publicProcedure
|
|
.input(z.object({ token: z.string() }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const { token } = input;
|
|
const userId = ctx.user?.userId;
|
|
|
|
if (userId) {
|
|
// AUTHENTICATED USER
|
|
// Check if token exists in notif_creds for this user
|
|
const existing = await db.query.notifCreds.findFirst({
|
|
where: and(
|
|
eq(notifCreds.userId, userId),
|
|
eq(notifCreds.token, token)
|
|
),
|
|
});
|
|
|
|
if (existing) {
|
|
// Update lastVerified timestamp
|
|
await db
|
|
.update(notifCreds)
|
|
.set({ lastVerified: new Date() })
|
|
.where(eq(notifCreds.id, existing.id));
|
|
} else {
|
|
// Insert new token into notif_creds
|
|
await db.insert(notifCreds).values({
|
|
userId,
|
|
token,
|
|
lastVerified: new Date(),
|
|
});
|
|
}
|
|
|
|
// Remove from unlogged_user_tokens if it exists
|
|
await db
|
|
.delete(unloggedUserTokens)
|
|
.where(eq(unloggedUserTokens.token, token));
|
|
|
|
} else {
|
|
// UNAUTHENTICATED USER
|
|
// Save/update in unlogged_user_tokens
|
|
const existing = await db.query.unloggedUserTokens.findFirst({
|
|
where: eq(unloggedUserTokens.token, token),
|
|
});
|
|
|
|
if (existing) {
|
|
await db
|
|
.update(unloggedUserTokens)
|
|
.set({ lastVerified: new Date() })
|
|
.where(eq(unloggedUserTokens.id, existing.id));
|
|
} else {
|
|
await db.insert(unloggedUserTokens).values({
|
|
token,
|
|
lastVerified: new Date(),
|
|
});
|
|
}
|
|
}
|
|
|
|
return { success: true };
|
|
}),
|
|
}); |