feat: telegram bot unbind && delete address (#254)

This commit is contained in:
Dream Hunter
2024-05-20 13:23:41 +08:00
committed by GitHub
parent c00382259a
commit 69771fc1d1
19 changed files with 261 additions and 89 deletions

View File

@@ -3,6 +3,7 @@
## main branch
- telegram mini app
- telegram mini 增加 `ubind`, `delete` 指令
## v0.4.3

View File

@@ -1,6 +1,6 @@
{
"name": "cloudflare_temp_email",
"version": "0.4.3",
"version": "0.4.4",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,16 +1,14 @@
import { Context } from "hono";
import { Bindings } from "../types";
import { HonoCustomType } from "../types";
import { CONSTANTS } from "../constants";
import { AdminWebhookSettings } from "../models/models";
// @ts-ignore
import { getBooleanValue } from "../utils";
async function getWebhookSettings(c: Context<{ Bindings: Bindings }>): Promise<Response> {
async function getWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
const settings = await c.env.KV.get<AdminWebhookSettings>(CONSTANTS.WEBHOOK_KV_SETTINGS_KEY, "json");
return c.json(settings || new AdminWebhookSettings([]));
}
async function saveWebhookSettings(c: Context<{ Bindings: Bindings }>): Promise<Response> {
async function saveWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
const settings = await c.req.json<AdminWebhookSettings>();
await c.env.KV.put(CONSTANTS.WEBHOOK_KV_SETTINGS_KEY, JSON.stringify(settings));
return c.json({ success: true })

View File

@@ -1,11 +1,10 @@
import { Hono } from 'hono'
// @ts-ignore
import { getDomains, getPasswords, getBooleanValue } from './utils';
import { CONSTANTS } from './constants';
import { Bindings } from './types';
import { HonoCustomType } from './types';
const api = new Hono<{ Bindings: Bindings }>
const api = new Hono<HonoCustomType>
api.get('/open_api/settings', async (c) => {
// check header x-custom-auth
@@ -13,7 +12,7 @@ api.get('/open_api/settings', async (c) => {
const passwords = getPasswords(c);
if (passwords && passwords.length > 0) {
const auth = c.req.raw.headers.get("x-custom-auth");
needAuth = !passwords.includes(auth);
needAuth = !auth || !passwords.includes(auth);
}
return c.json({
"prefix": c.env.PREFIX,

View File

@@ -1,8 +1,16 @@
import { Context } from 'hono';
import { Jwt } from 'hono/utils/jwt'
import { getDomains, getStringValue } from './utils';
import { HonoCustomType } from './types';
import { CONSTANTS } from './constants';
import { unbindTelegramByAddress } from './telegram_api/common';
export const newAddress = async (c, name, domain, enablePrefix) => {
export const newAddress = async (
c: Context<HonoCustomType>,
name: string, domain: string | undefined | null,
enablePrefix: boolean
): Promise<{ address: string, jwt: string }> => {
// remove special characters
name = name.replace(/[^a-zA-Z0-9.]/g, '')
// check name length
@@ -36,10 +44,9 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
}
throw new Error("Failed to create address")
}
let address_id = 0;
address_id = await c.env.DB.prepare(
const address_id = await c.env.DB.prepare(
`SELECT id FROM address where name = ?`
).bind(name).first("id");
).bind(name).first<number>("id");
// create jwt
const jwt = await Jwt.sign({
address: name,
@@ -51,7 +58,11 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
}
}
export const cleanup = async (c, cleanType, cleanDays) => {
export const cleanup = async (
c: Context<HonoCustomType>,
cleanType: string | undefined | null,
cleanDays: number | undefined | null
): Promise<boolean> => {
if (!cleanType || !cleanDays || cleanDays < 0 || cleanDays > 30) {
throw new Error("Invalid cleanType or cleanDays")
}
@@ -80,17 +91,57 @@ export const cleanup = async (c, cleanType, cleanDays) => {
}
/**
*
* @param {*} c context
* @param {*} query @type {string} query
* @param {*} countQuery @type {string} countQuery
* @param {*} limit @type {number} limit
* @param {*} offset @type {number} offset
* @returns {Promise} Promise
* TODO: need senbox delete?
*/
export const deleteAddressWithData = async (
c: Context<HonoCustomType>,
address: string | undefined | null,
address_id: number | undefined | null
): Promise<boolean> => {
if (!address && !address_id) {
throw new Error("Address or address_id required")
}
// get address_id or address
if (!address_id) {
address_id = await c.env.DB.prepare(
`SELECT id FROM address where name = ?`
).bind(address).first<number>("id");
} else if (!address) {
address = await c.env.DB.prepare(
`SELECT name FROM address where id = ?`
).bind(address_id).first<string>("name");
}
// check address again
if (!address || !address_id) {
throw new Error("Can't find address");
}
// unbind telegram
await unbindTelegramByAddress(c, address);
// delete address and related data
const { success: mailSuccess } = await c.env.DB.prepare(
`DELETE FROM raw_mails WHERE address = ? `
).bind(address).run();
const { success: sendAccess } = await c.env.DB.prepare(
`DELETE FROM address_sender WHERE address = ? `
).bind(address).run();
const { success: addressSuccess } = await c.env.DB.prepare(
`DELETE FROM users_address WHERE address_id = ? `
).bind(address_id).run();
const { success } = await c.env.DB.prepare(
`DELETE FROM address WHERE name = ? `
).bind(address).run();
if (!success || !mailSuccess || !addressSuccess || !sendAccess) {
throw new Error("Failed to delete address")
}
return true;
}
export const handleListQuery = async (
c, query, countQuery, params, limit, offset
) => {
c: Context<HonoCustomType>,
query: string, countQuery: string, params: string[],
limit: number | undefined | null,
offset: number | undefined | null
): Promise<Response> => {
if (!limit || limit < 0 || limit > 100) {
return c.text("Invalid limit", 400)
}

View File

@@ -1,5 +1,5 @@
export const CONSTANTS = {
VERSION: 'v0.4.3',
VERSION: 'v0.4.4',
// DB settings
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',

View File

@@ -1,5 +1,4 @@
import { createMimeMessage } from "mimetext";
// @ts-ignore
import { getBooleanValue } from "../utils";
import { Bindings } from "../types";

View File

@@ -1,7 +1,7 @@
import { Context } from "hono";
import { sendMailToTelegram } from "../telegram_api";
import { Bindings } from "../types";
import { Bindings, HonoCustomType } from "../types";
import { auto_reply } from "./auto_reply";
import { trigerWebhook } from "../mails_api/webhook_settings";
@@ -28,7 +28,7 @@ async function email(message: ForwardableEmailMessage, env: Bindings, ctx: Execu
// send email to telegram
try {
await sendMailToTelegram(
{ env: env } as Context<{ Bindings: Bindings }>,
{ env: env } as Context<HonoCustomType>,
message.to, rawEmail);
} catch (error) {
console.log("send mail to telegram error", error);
@@ -37,7 +37,7 @@ async function email(message: ForwardableEmailMessage, env: Bindings, ctx: Execu
// send webhook
try {
await trigerWebhook(
{ env: env } as Context<{ Bindings: Bindings }>,
{ env: env } as Context<HonoCustomType>,
message.to, rawEmail
);
} catch (error) {

View File

@@ -1,8 +1,7 @@
import { Context } from "hono";
import { Bindings, Variables } from "../types";
import { HonoCustomType } from "../types";
import { CONSTANTS } from "../constants";
import { AdminWebhookSettings, WebhookMail } from "../models/models";
// @ts-ignore
import { getBooleanValue } from "../utils";
import PostalMime from 'postal-mime';
@@ -24,9 +23,7 @@ class WebhookSettings {
}
async function getWebhookSettings(
c: Context<{ Bindings: Bindings, Variables: Variables }>
): Promise<Response> {
async function getWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
if (!c.env.KV) {
return c.text("KV is not available", 400);
}
@@ -45,9 +42,7 @@ async function getWebhookSettings(
}
async function saveWebhookSettings(
c: Context<{ Bindings: Bindings, Variables: Variables }>
): Promise<Response> {
async function saveWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
const { address } = c.get("jwtPayload")
const adminSettings = await c.env.KV.get<AdminWebhookSettings>(CONSTANTS.WEBHOOK_KV_SETTINGS_KEY, "json");
if (!adminSettings?.allowList.includes(address)) {
@@ -79,7 +74,7 @@ async function sendWebhook(settings: WebhookSettings, formatMap: WebhookMail): P
}
export async function trigerWebhook(
c: Context<{ Bindings: Bindings }>,
c: Context<HonoCustomType>,
address: string,
raw_mail: string
): Promise<void> {
@@ -110,9 +105,7 @@ export async function trigerWebhook(
}
}
async function testWebhookSettings(
c: Context<{ Bindings: Bindings, Variables: Variables }>
): Promise<Response> {
async function testWebhookSettings(c: Context<HonoCustomType>): Promise<Response> {
const settings = await c.req.json<WebhookSettings>();
const res = await sendWebhook(settings, {
from: "from@test.com",

View File

@@ -14,3 +14,27 @@ export type WebhookMail = {
raw: string;
parsedText: string;
}
export class CleanupSettings {
enableMailsAutoCleanup: boolean | undefined;
cleanMailsDays: number;
enableUnknowMailsAutoCleanup: boolean | undefined;
cleanUnknowMailsDays: number;
enableSendBoxAutoCleanup: boolean | undefined;
cleanSendBoxDays: number;
constructor(data: CleanupSettings | undefined | null) {
const {
enableMailsAutoCleanup, cleanMailsDays,
enableUnknowMailsAutoCleanup, cleanUnknowMailsDays,
enableSendBoxAutoCleanup, cleanSendBoxDays
} = data || {};
this.enableMailsAutoCleanup = enableMailsAutoCleanup;
this.cleanMailsDays = cleanMailsDays || 0;
this.enableUnknowMailsAutoCleanup = enableUnknowMailsAutoCleanup;
this.cleanUnknowMailsDays = cleanUnknowMailsDays || 0;
this.enableSendBoxAutoCleanup = enableSendBoxAutoCleanup;
this.cleanSendBoxDays = cleanSendBoxDays || 0;
}
}

View File

@@ -1,33 +1,35 @@
import { Context } from 'hono';
import { cleanup } from './common'
import { CONSTANTS } from './constants'
import { getJsonSetting } from './utils';
import { CleanupSettings } from './models';
import { CleanupSettings } from './models/models';
import { Bindings, HonoCustomType } from './types';
export async function scheduled(event, env, ctx) {
export async function scheduled(event: ScheduledEvent, env: Bindings, ctx: any) {
console.log("Scheduled event: ", event);
const value = await getJsonSetting(
{ env: env, },
{ env: env, } as Context<HonoCustomType>,
CONSTANTS.AUTO_CLEANUP_KEY
);
const autoCleanupSetting = new CleanupSettings(value);
console.log("autoCleanupSetting:", JSON.stringify(autoCleanupSetting));
if (autoCleanupSetting.enableMailsAutoCleanup && autoCleanupSetting.cleanMailsDays > 0) {
await cleanup(
{ env: env, },
{ env: env, } as Context<HonoCustomType>,
"mails",
autoCleanupSetting.cleanMailsDays
);
}
if (autoCleanupSetting.enableUnknowMailsAutoCleanup && autoCleanupSetting.cleanUnknowMailsDays > 0) {
await cleanup(
{ env: env, },
{ env: env, } as Context<HonoCustomType>,
"mails_unknow",
autoCleanupSetting.cleanUnknowMailsDays
);
}
if (autoCleanupSetting.enableSendBoxAutoCleanup && autoCleanupSetting.cleanSendBoxDays > 0) {
await cleanup(
{ env: env, },
{ env: env, } as Context<HonoCustomType>,
"sendbox",
autoCleanupSetting.cleanSendBoxDays
);

View File

@@ -0,0 +1,35 @@
import { Context } from "hono";
import { Jwt } from "hono/utils/jwt";
import { CONSTANTS } from "../constants";
import { HonoCustomType } from "../types";
export const unbindTelegramAddress = async (
c: Context<HonoCustomType>, userId: string, address: string
): Promise<boolean> => {
const jwtList = await c.env.KV.get<string[]>(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, 'json') || [];
const newJwtList = [];
for (const jwt of jwtList) {
try {
const { address: kvAddress } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
if (kvAddress == address) {
continue;
}
} catch (e) {
console.log(`解绑失败: ${(e as Error).message}`);
}
newJwtList.push(jwt);
}
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, JSON.stringify(newJwtList));
await c.env.KV.delete(`${CONSTANTS.TG_KV_PREFIX}:${address}`);
return true;
}
export const unbindTelegramByAddress = async (
c: Context<HonoCustomType>, address: string
): Promise<boolean> => {
const userId = await c.env.KV.get<string>(`${CONSTANTS.TG_KV_PREFIX}:${address}`)
if (userId) {
return await unbindTelegramAddress(c, userId, address);
}
return true;
}

View File

@@ -2,12 +2,12 @@ import { Hono } from 'hono'
import { ServerResponse } from 'node:http'
import { Writable } from 'node:stream'
import { Bindings } from '../types'
import { HonoCustomType } from '../types'
import { newTelegramBot, initTelegramBotCommands, sendMailToTelegram } from './telegram'
import settings from './settings'
import miniapp from './miniapp'
export const api = new Hono<{ Bindings: Bindings }>();
export const api = new Hono<HonoCustomType>();
export { sendMailToTelegram }
api.use("/telegram/*", async (c, next) => {

View File

@@ -1,12 +1,12 @@
import { Context } from "hono";
import { Jwt } from 'hono/utils/jwt'
import { Bindings } from "../types";
import { HonoCustomType } from "../types";
import { CONSTANTS } from "../constants";
const encoder = new TextEncoder();
async function getTelegramBindAddress(c: Context<{ Bindings: Bindings }>): Promise<Response> {
async function getTelegramBindAddress(c: Context<HonoCustomType>): Promise<Response> {
// check if the request is from telegram
const { initData } = await c.req.json();
const initDataObj = new URLSearchParams(initData);

View File

@@ -1,5 +1,5 @@
import { Context } from "hono";
import { Bindings } from "../types";
import { HonoCustomType } from "../types";
import { CONSTANTS } from "../constants";
export class TelegramSettings {
@@ -12,13 +12,13 @@ export class TelegramSettings {
}
}
async function getTelegramSettings(c: Context<{ Bindings: Bindings }>): Promise<Response> {
async function getTelegramSettings(c: Context<HonoCustomType>): Promise<Response> {
const settings = await c.env.KV.get<TelegramSettings>(CONSTANTS.TG_KV_SETTINGS_KEY, "json");
return c.json(settings || new TelegramSettings(false, []));
}
async function saveTelegramSettings(c: Context<{ Bindings: Bindings }>): Promise<Response> {
async function saveTelegramSettings(c: Context<HonoCustomType>): Promise<Response> {
const settings = await c.req.json<TelegramSettings>();
await c.env.KV.put(CONSTANTS.TG_KV_SETTINGS_KEY, JSON.stringify(settings));
return c.json({ success: true })

View File

@@ -6,12 +6,12 @@ import { callbackQuery } from "telegraf/filters";
import PostalMime from 'postal-mime';
import { CONSTANTS } from "../constants";
// @ts-ignore
import { getIntValue, getDomains, getStringValue } from '../utils';
// @ts-ignore
import { newAddress } from '../common'
import { Bindings } from "../types";
import { deleteAddressWithData, newAddress } from '../common'
import { HonoCustomType } from "../types";
import { TelegramSettings } from "./settings";
import { unbindTelegramAddress } from "./common";
const COMMANDS = [
{
@@ -30,13 +30,21 @@ const COMMANDS = [
command: "bind",
description: "绑定邮箱地址, 请输入 /bind <邮箱地址凭证>"
},
{
command: "unbind",
description: "解绑邮箱地址, 请输入 /unbind <邮箱地址>"
},
{
command: "delete",
description: "删除邮箱地址, 请输入 /delete <邮箱地址>"
},
{
command: "mails",
description: "查看邮件, 请输入 /mails <邮箱地址>, 不输入地址默认查看第一个地址"
},
]
export function newTelegramBot(c: Context<{ Bindings: Bindings }>, token: string): Telegraf {
export function newTelegramBot(c: Context<HonoCustomType>, token: string): Telegraf {
const bot = new Telegraf(token);
bot.use(async (ctx, next) => {
@@ -55,8 +63,12 @@ export function newTelegramBot(c: Context<{ Bindings: Bindings }>, token: string
) {
return await ctx.reply("您没有权限使用此机器人");
}
await next();
try {
await next();
} catch (error) {
console.error(`Error: ${error}`);
return await ctx.reply(`Error: ${error}`);
}
})
bot.command("start", async (ctx: TgContext) => {
@@ -65,8 +77,6 @@ export function newTelegramBot(c: Context<{ Bindings: Bindings }>, token: string
return await ctx.reply(
"欢迎使用本机器人, 您可以点击左下角打开 mini app \n\n"
+ (prefix ? `当前已启用前缀: ${prefix}\n` : '')
+ "新建邮箱地址, 如果要自定义邮箱地址, "
+ "请输入 /new <name>@<domain>, name [a-zA-Z0-9.] 有效\n"
+ `当前可用域名: ${JSON.stringify(domains)}\n`
+ "请使用以下命令:\n"
+ COMMANDS.map(c => `/${c.command}: ${c.description}`).join("\n")
@@ -137,6 +147,40 @@ export function newTelegramBot(c: Context<{ Bindings: Bindings }>, token: string
}
});
bot.command("unbind", async (ctx: TgContext) => {
const userId = ctx?.message?.from?.id;
if (!userId) {
return await ctx.reply("无法获取用户信息");
}
try {
// @ts-ignore
const address = ctx?.message?.text.slice("/unbind".length).trim();
if (!address) {
return await ctx.reply("请输入地址");
}
await unbindTelegramAddress(c, userId.toString(), address);
return await ctx.reply(`解绑成功:\n地址: ${address}`
);
}
catch (e) {
return await ctx.reply(`解绑失败: ${(e as Error).message}`);
}
})
bot.command("delete", async (ctx: TgContext) => {
try {
// @ts-ignore
const address = ctx?.message?.text.slice("/unbind".length).trim();
if (!address) {
return await ctx.reply("请输入地址");
}
await deleteAddressWithData(c, address, null)
return await ctx.reply(`删除成功: ${address}`);
} catch (e) {
return await ctx.reply(`删除失败: ${(e as Error).message}`);
}
});
bot.command("address", async (ctx) => {
const userId = ctx?.message?.from?.id;
if (!userId) {
@@ -251,7 +295,7 @@ const parseMail = async (raw_mail: string | undefined | null) => {
try {
const parsedEmail = await PostalMime.parse(raw_mail);
if (parsedEmail?.text?.length && parsedEmail?.text?.length > 1000) {
parsedEmail.text = parsedEmail.text.substring(0, 1000) + "...";
parsedEmail.text = parsedEmail.text.substring(0, 1000) + "...消息过长请到miniapp查看";
}
return {
isHtml: false,
@@ -270,7 +314,7 @@ const parseMail = async (raw_mail: string | undefined | null) => {
}
export async function sendMailToTelegram(c: Context<{ Bindings: Bindings }>, address: string, raw_mail: string) {
export async function sendMailToTelegram(c: Context<HonoCustomType>, address: string, raw_mail: string) {
if (!c.env.TELEGRAM_BOT_TOKEN || !c.env.KV) {
return;
}

View File

@@ -6,6 +6,9 @@ export type Bindings = {
// config
PREFIX: string | undefined
DOMAINS: string | string[] | undefined
PASSWORDS: string | string[] | undefined
ADMIN_PASSWORDS: string | string[] | undefined
JWT_SECRET: string
BLACK_LIST: string | undefined
ENABLE_AUTO_REPLY: string | boolean | undefined
@@ -18,6 +21,7 @@ export type Bindings = {
// cf turnstile
CF_TURNSTILE_SITE_KEY: string | undefined
CF_TURNSTILE_SECRET_KEY: string | undefined
// telegram config
TELEGRAM_BOT_TOKEN: string
@@ -40,3 +44,8 @@ type Variables = {
userPayload: UserPayload,
jwtPayload: JwtPayload
}
type HonoCustomType = {
"Bindings": Bindings;
"Variables": Variables;
}

View File

@@ -1,6 +1,10 @@
import { Context } from "hono";
import { createMimeMessage } from "mimetext";
import { HonoCustomType } from "./types";
export const getJsonSetting = async (c, key) => {
export const getJsonSetting = async (
c: Context<HonoCustomType>, key: string
): Promise<any> => {
const value = await getSetting(c, key);
if (!value) {
return null;
@@ -13,11 +17,13 @@ export const getJsonSetting = async (c, key) => {
return null;
}
export const getSetting = async (c, key) => {
export const getSetting = async (
c: Context<HonoCustomType>, key: string
): Promise<string | null> => {
try {
const value = await c.env.DB.prepare(
`SELECT value FROM settings where key = ?`
).bind(key).first("value");
).bind(key).first<string>("value");
return value;
} catch (error) {
console.error(`GetSetting: Failed to get ${key}`, error);
@@ -25,7 +31,10 @@ export const getSetting = async (c, key) => {
return null;
}
export const saveSetting = async (c, key, value) => {
export const saveSetting = async (
c: Context<HonoCustomType>,
key: string, value: string
) => {
await c.env.DB.prepare(
`INSERT or REPLACE INTO settings (key, value) VALUES (?, ?)`
+ ` ON CONFLICT(key) DO UPDATE SET value = ?, updated_at = datetime('now')`
@@ -33,14 +42,16 @@ export const saveSetting = async (c, key, value) => {
return true;
}
export const getStringValue = (value) => {
export const getStringValue = (value: any): string => {
if (typeof value === "string") {
return value;
}
return "";
}
export const getBooleanValue = (value) => {
export const getBooleanValue = (
value: boolean | string | any
): boolean => {
if (typeof value === "boolean") {
return value;
}
@@ -51,7 +62,10 @@ export const getBooleanValue = (value) => {
return false;
}
export const getIntValue = (value, defaultValue = 0) => {
export const getIntValue = (
value: number | string | any,
defaultValue: number = 0
): number => {
if (typeof value === "number") {
return value;
}
@@ -65,7 +79,7 @@ export const getIntValue = (value, defaultValue = 0) => {
return defaultValue;
}
export const getDomains = (c) => {
export const getDomains = (c: Context<HonoCustomType>): string[] => {
if (!c.env.DOMAINS) {
return [];
}
@@ -81,14 +95,14 @@ export const getDomains = (c) => {
return c.env.DOMAINS;
}
export const getPasswords = (c) => {
export const getPasswords = (c: Context<HonoCustomType>): string[] => {
if (!c.env.PASSWORDS) {
return [];
}
// check if PASSWORDS is an array, if not use json.parse
if (!Array.isArray(c.env.PASSWORDS)) {
try {
let res = JSON.parse(c.env.PASSWORDS);
const res = JSON.parse(c.env.PASSWORDS) as string[];
return res.filter((item) => item.length > 0);
} catch (e) {
console.error("Failed to parse PASSWORDS", e);
@@ -98,23 +112,26 @@ export const getPasswords = (c) => {
return c.env.PASSWORDS.filter((item) => item.length > 0);
}
export const getAdminPasswords = (c) => {
export const getAdminPasswords = (c: Context<HonoCustomType>): string[] => {
if (!c.env.ADMIN_PASSWORDS) {
return [];
}
// check if ADMIN_PASSWORDS is an array, if not use json.parse
if (!Array.isArray(c.env.ADMIN_PASSWORDS)) {
try {
return JSON.parse(c.env.ADMIN_PASSWORDS);
const res = JSON.parse(c.env.ADMIN_PASSWORDS) as string[];
return res.filter((item) => item.length > 0);
} catch (e) {
console.error("Failed to parse ADMIN_PASSWORDS", e);
return [];
}
}
return c.env.ADMIN_PASSWORDS;
return c.env.ADMIN_PASSWORDS.filter((item) => item.length > 0);
}
export const sendAdminInternalMail = async (c, toMail, subject, text) => {
export const sendAdminInternalMail = async (
c: Context<HonoCustomType>, toMail: string, subject: string, text: string
): Promise<boolean> => {
try {
const msg = createMimeMessage();
@@ -144,31 +161,33 @@ export const sendAdminInternalMail = async (c, toMail, subject, text) => {
}
};
export const checkCfTurnstile = async (c, token) => {
if (!c.env.CF_TURNSTILE_SITE_KEY) {
export const checkCfTurnstile = async (
c: Context<HonoCustomType>, token: string | undefined | null
): Promise<void> => {
if (!c.env.CF_TURNSTILE_SITE_KEY || !c.env.CF_TURNSTILE_SECRET_KEY) {
return;
}
if (!token) {
throw new Error("Captcha token is required");
}
const reqIp = c.req.raw.headers.get("cf-connecting-ip")
const reqIp = c.req.raw.headers.get("cf-connecting-ip");
let formData = new FormData();
formData.append('secret', c.env.CF_TURNSTILE_SECRET_KEY);
formData.append('response', token);
formData.append('remoteip', reqIp);
if (reqIp) formData.append('remoteip', reqIp);
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
const result = await fetch(url, {
body: formData,
method: 'POST',
});
const captchaRes = await result.json();
const captchaRes: any = await result.json();
if (!captchaRes.success) {
console.log("Captcha failed", captchaRes);
throw new Error("Captcha failed");
}
}
export const checkUserPassword = (password) => {
export const checkUserPassword = (password: string) => {
if (!password || password.length < 1 || password.length > 100) {
throw new Error("Invalid password")
}

View File

@@ -3,7 +3,6 @@ import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt'
import { Jwt } from 'hono/utils/jwt'
// @ts-ignore
import { api as commonApi } from './commom_api';
// @ts-ignore
import { api as mailsApi } from './mails_api'
@@ -18,13 +17,11 @@ import { api as apiSendMail } from './mails_api/send_mail_api'
import { api as telegramApi } from './telegram_api'
import { email } from './email';
// @ts-ignore
import { scheduled } from './scheduled';
// @ts-ignore
import { getAdminPasswords, getPasswords, getBooleanValue } from './utils';
import { Bindings } from './types';
import { HonoCustomType } from './types';
const app = new Hono<{ Bindings: Bindings }>()
const app = new Hono<HonoCustomType>()
//cors
app.use('/*', cors());
// rate limit
@@ -88,6 +85,7 @@ app.use('/user_api/*', async (c, next) => {
}
try {
const token = c.req.raw.headers.get("x-user-token");
if (!token) return c.text("Need User Token", 401)
const payload = await Jwt.verify(token, c.env.JWT_SECRET, "HS256");
// check expired
if (!payload.exp) return c.text("Invalid Token", 401);