79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { refreshAccessToken } from './microsoft-oauth';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { getRedisClient, KEYS } from '@/lib/redis';
|
|
|
|
/**
|
|
* Check if a token is expired or about to expire (within 5 minutes)
|
|
*/
|
|
export function isTokenExpired(expiryTimestamp: number): boolean {
|
|
const fiveMinutesInMs = 5 * 60 * 1000;
|
|
return Date.now() + fiveMinutesInMs >= expiryTimestamp;
|
|
}
|
|
|
|
/**
|
|
* Refresh an access token if it's expired or about to expire
|
|
*/
|
|
export async function ensureFreshToken(
|
|
userId: string,
|
|
email: string
|
|
): Promise<{ accessToken: string; success: boolean }> {
|
|
try {
|
|
// Get stored credentials using raw query until Prisma schema is updated
|
|
const credentials = await prisma.$queryRaw`
|
|
SELECT "useOAuth", "refreshToken", "accessToken", "tokenExpiry"
|
|
FROM "MailCredentials"
|
|
WHERE "userId" = ${userId} AND "email" = ${email}
|
|
LIMIT 1
|
|
`;
|
|
|
|
const credData = Array.isArray(credentials) && credentials.length > 0
|
|
? credentials[0]
|
|
: null;
|
|
|
|
// If not OAuth or missing refresh token, return failure
|
|
if (!credData?.useOAuth || !credData.refreshToken) {
|
|
return { accessToken: '', success: false };
|
|
}
|
|
|
|
// If token is still valid, return current token
|
|
if (credData.tokenExpiry && credData.accessToken &&
|
|
new Date(credData.tokenExpiry) > new Date(Date.now() + 5 * 60 * 1000)) {
|
|
return { accessToken: credData.accessToken, success: true };
|
|
}
|
|
|
|
// Token is expired or about to expire, refresh it
|
|
console.log(`Refreshing token for user ${userId}, account ${email}`);
|
|
const tokens = await refreshAccessToken(credData.refreshToken);
|
|
|
|
// Update database with new token information using raw query
|
|
await prisma.$executeRaw`
|
|
UPDATE "MailCredentials"
|
|
SET
|
|
"accessToken" = ${tokens.access_token},
|
|
"refreshToken" = ${tokens.refresh_token || credData.refreshToken},
|
|
"tokenExpiry" = ${new Date(Date.now() + (tokens.expires_in * 1000))}
|
|
WHERE "userId" = ${userId} AND "email" = ${email}
|
|
`;
|
|
|
|
// Update Redis cache
|
|
const redis = getRedisClient();
|
|
const key = KEYS.CREDENTIALS(userId, email);
|
|
const credStr = await redis.get(key);
|
|
|
|
if (credStr) {
|
|
const creds = JSON.parse(credStr);
|
|
creds.accessToken = tokens.access_token;
|
|
if (tokens.refresh_token) {
|
|
creds.refreshToken = tokens.refresh_token;
|
|
}
|
|
creds.tokenExpiry = Date.now() + (tokens.expires_in * 1000);
|
|
|
|
await redis.set(key, JSON.stringify(creds), 'EX', 86400); // 24 hours
|
|
}
|
|
|
|
return { accessToken: tokens.access_token, success: true };
|
|
} catch (error) {
|
|
console.error(`Error refreshing token for user ${userId}:`, error);
|
|
return { accessToken: '', success: false };
|
|
}
|
|
}
|