Stop ticking Telegram login message
All checks were successful
Build and deploy Backend / build (push) Successful in 28s
All checks were successful
Build and deploy Backend / build (push) Successful in 28s
This commit is contained in:
@@ -38,7 +38,7 @@ type TelegramSentMessage = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loginPrefix = 'login_';
|
const loginPrefix = 'login_';
|
||||||
const loginTimerHandles = new Map<string, ReturnType<typeof setTimeout>>();
|
const loginExpirationHandles = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
function randomToken() {
|
function randomToken() {
|
||||||
return randomBytes(32).toString('base64url');
|
return randomBytes(32).toString('base64url');
|
||||||
@@ -124,12 +124,12 @@ function formatRemaining(expiresAt: Date) {
|
|||||||
return `${minutes}:${rest}`;
|
return `${minutes}:${rest}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loginReplyMarkup(token: string, expiresAt: Date) {
|
function loginReplyMarkup(token: string) {
|
||||||
return {
|
return {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: 'Открыть MapFlow',
|
text: 'Войти',
|
||||||
url: `${config.webAppUrl}?telegram_login=${encodeURIComponent(token)}`,
|
url: `${config.webAppUrl}?telegram_login=${encodeURIComponent(token)}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -138,12 +138,7 @@ function loginReplyMarkup(token: string, expiresAt: Date) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loginMessageText(expiresAt: Date) {
|
function loginMessageText(expiresAt: Date) {
|
||||||
return [
|
return [formatRemaining(expiresAt), '', 'Войти в MapFlow'].join('\n');
|
||||||
'Вход в MapFlow',
|
|
||||||
'',
|
|
||||||
'Перейдите по ссылке ниже.',
|
|
||||||
`Ссылка активна: ${formatRemaining(expiresAt)}`,
|
|
||||||
].join('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendLoginMessage(
|
async function sendLoginMessage(
|
||||||
@@ -152,7 +147,7 @@ async function sendLoginMessage(
|
|||||||
token?: string,
|
token?: string,
|
||||||
expiresAt?: Date,
|
expiresAt?: Date,
|
||||||
) {
|
) {
|
||||||
const replyMarkup = token && expiresAt ? loginReplyMarkup(token, expiresAt) : undefined;
|
const replyMarkup = token && expiresAt ? loginReplyMarkup(token) : undefined;
|
||||||
|
|
||||||
return callTelegram<TelegramSentMessage>('sendMessage', {
|
return callTelegram<TelegramSentMessage>('sendMessage', {
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
@@ -165,67 +160,79 @@ async function editLoginMessage(
|
|||||||
chatId: string,
|
chatId: string,
|
||||||
messageId: number,
|
messageId: number,
|
||||||
text: string,
|
text: string,
|
||||||
token?: string,
|
|
||||||
expiresAt?: Date,
|
|
||||||
) {
|
) {
|
||||||
const replyMarkup = token && expiresAt
|
|
||||||
? loginReplyMarkup(token, expiresAt)
|
|
||||||
: { inline_keyboard: [] };
|
|
||||||
await callTelegram('editMessageText', {
|
await callTelegram('editMessageText', {
|
||||||
chat_id: chatId,
|
chat_id: chatId,
|
||||||
message_id: messageId,
|
message_id: messageId,
|
||||||
text,
|
text,
|
||||||
reply_markup: replyMarkup,
|
reply_markup: { inline_keyboard: [] },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function scheduleLoginMessageTimer(
|
function scheduleLoginExpiration(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
token: string,
|
|
||||||
chatId: string,
|
chatId: string,
|
||||||
messageId: number,
|
messageId: number,
|
||||||
expiresAt: Date,
|
expiresAt: Date,
|
||||||
) {
|
) {
|
||||||
const existingHandle = loginTimerHandles.get(requestId);
|
const existingHandle = loginExpirationHandles.get(requestId);
|
||||||
if (existingHandle) {
|
if (existingHandle) {
|
||||||
clearTimeout(existingHandle);
|
clearTimeout(existingHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tick = async () => {
|
const handle = setTimeout(() => {
|
||||||
const request = await prisma.telegramLoginRequest.findUnique({
|
void (async () => {
|
||||||
where: { id: requestId },
|
|
||||||
select: { status: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!request || request.status !== 'CONFIRMED') {
|
|
||||||
loginTimerHandles.delete(requestId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expiresAt <= new Date()) {
|
|
||||||
const updated = await prisma.telegramLoginRequest.updateMany({
|
const updated = await prisma.telegramLoginRequest.updateMany({
|
||||||
where: { id: requestId, status: 'CONFIRMED' },
|
where: { id: requestId, status: 'CONFIRMED' },
|
||||||
data: { status: 'EXPIRED' },
|
data: { status: 'EXPIRED' },
|
||||||
});
|
});
|
||||||
|
|
||||||
loginTimerHandles.delete(requestId);
|
loginExpirationHandles.delete(requestId);
|
||||||
if (updated.count > 0) {
|
if (updated.count > 0) {
|
||||||
await editLoginMessage(chatId, messageId, 'Ссылка входа устарела.');
|
await editLoginMessage(chatId, messageId, 'Ссылка входа устарела.');
|
||||||
}
|
}
|
||||||
return;
|
})();
|
||||||
|
}, Math.max(0, expiresAt.getTime() - Date.now()));
|
||||||
|
loginExpirationHandles.set(requestId, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
await editLoginMessage(chatId, messageId, loginMessageText(expiresAt), token, expiresAt);
|
function cancelLoginExpiration(requestId: string) {
|
||||||
const handle = setTimeout(() => {
|
const handle = loginExpirationHandles.get(requestId);
|
||||||
void tick();
|
if (handle) {
|
||||||
}, 1000);
|
clearTimeout(handle);
|
||||||
loginTimerHandles.set(requestId, handle);
|
loginExpirationHandles.delete(requestId);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handle = setTimeout(() => {
|
async function expireLoginMessageNow(
|
||||||
void tick();
|
requestId: string,
|
||||||
}, 1000);
|
chatId: string,
|
||||||
loginTimerHandles.set(requestId, handle);
|
messageId: number,
|
||||||
|
) {
|
||||||
|
cancelLoginExpiration(requestId);
|
||||||
|
const updated = await prisma.telegramLoginRequest.updateMany({
|
||||||
|
where: { id: requestId, status: 'CONFIRMED' },
|
||||||
|
data: { status: 'EXPIRED' },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updated.count > 0) {
|
||||||
|
await editLoginMessage(chatId, messageId, 'Ссылка входа устарела.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshExpiredLoginMessage(
|
||||||
|
requestId: string,
|
||||||
|
chatId: string,
|
||||||
|
messageId: number,
|
||||||
|
) {
|
||||||
|
const request = await prisma.telegramLoginRequest.findUnique({
|
||||||
|
where: { id: requestId },
|
||||||
|
select: { status: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (request?.status === 'CONFIRMED') {
|
||||||
|
await expireLoginMessageNow(requestId, chatId, messageId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createTelegramBotLogin() {
|
export async function createTelegramBotLogin() {
|
||||||
@@ -257,10 +264,18 @@ export async function completeTelegramBotLogin(token: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.expiresAt <= new Date()) {
|
if (request.expiresAt <= new Date()) {
|
||||||
|
if (request.telegramChatId && request.telegramMessageId) {
|
||||||
|
await refreshExpiredLoginMessage(
|
||||||
|
request.id,
|
||||||
|
request.telegramChatId,
|
||||||
|
request.telegramMessageId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
await prisma.telegramLoginRequest.update({
|
await prisma.telegramLoginRequest.update({
|
||||||
where: { id: request.id },
|
where: { id: request.id },
|
||||||
data: { status: 'EXPIRED' },
|
data: { status: 'EXPIRED' },
|
||||||
});
|
});
|
||||||
|
}
|
||||||
throw new Error('Telegram login token is expired.');
|
throw new Error('Telegram login token is expired.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +283,7 @@ export async function completeTelegramBotLogin(token: string) {
|
|||||||
throw new Error('Telegram login is not confirmed.');
|
throw new Error('Telegram login is not confirmed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelLoginExpiration(request.id);
|
||||||
await prisma.telegramLoginRequest.update({
|
await prisma.telegramLoginRequest.update({
|
||||||
where: { id: request.id },
|
where: { id: request.id },
|
||||||
data: { status: 'USED' },
|
data: { status: 'USED' },
|
||||||
@@ -314,7 +330,10 @@ export async function handleTelegramBotWebhook(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!request || request.status !== 'PENDING' || request.expiresAt <= new Date()) {
|
if (!request || request.status !== 'PENDING' || request.expiresAt <= new Date()) {
|
||||||
await sendLoginMessage(chatId, 'Ссылка входа устарела.\nВернитесь на сайт и начните вход заново.');
|
await sendLoginMessage(
|
||||||
|
chatId,
|
||||||
|
'Ссылка входа устарела.\nВернитесь на сайт и начните вход заново.',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,9 +368,8 @@ export async function handleTelegramBotWebhook(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
scheduleLoginMessageTimer(
|
scheduleLoginExpiration(
|
||||||
request.id,
|
request.id,
|
||||||
token,
|
|
||||||
chatId.toString(),
|
chatId.toString(),
|
||||||
sentMessage.message_id,
|
sentMessage.message_id,
|
||||||
request.expiresAt,
|
request.expiresAt,
|
||||||
|
|||||||
Reference in New Issue
Block a user