import { Jwt } from 'hono/utils/jwt' import { checkCfTurnstile, getJsonSetting, checkUserPassword } from "../utils" import { CONSTANTS } from "../constants"; import { GeoData, UserInfo, UserSettings } from "../models"; import { sendMail } from "../mails_api/send_mail_api"; export default { verifyCode: async (c) => { const { email, cf_token } = await c.req.json(); // check cf turnstile try { await checkCfTurnstile(c, cf_token); } catch (error) { return c.text("Failed to check cf turnstile", 500) } const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY); const settings = new UserSettings(value) // check mail domain allow list const mailDomain = email.split("@")[1]; if (settings.enableMailAllowList && settings.mailAllowList && !settings.mailAllowList.includes(mailDomain) ) { return c.text(`Mail domain must in ${JSON.stringify(settings.mailAllowList, null, 2)}`, 400) } // check if code exists in KV const tmpcode = await c.env.KV.get(`temp-mail:${email}`) if (tmpcode) { return c.text("Code already sent, please wait", 400) } // generate code 6 digits and convert to string const code = Math.floor(100000 + Math.random() * 900000).toString(); // send code to email try { await sendMail(c, settings.verifyMailSender, { to_mail: email, subject: "Temp Mail Verify code", content: `Your verify code is ${code}`, }) } catch (e) { return c.text(`Failed to send verify code: ${e.message}`, 500) } // save to KV await c.env.KV.put(`temp-mail:${email}`, code, { expirationTtl: 300 }); return c.json({ success: true, expirationTtl: 300 }) }, register: async (c) => { const value = await getJsonSetting(c, CONSTANTS.USER_SETTINGS_KEY); const settings = new UserSettings(value) // check enable if (!settings.enable) { return c.text("User registration is disabled"); } // check request const { email, password, code } = await c.req.json(); if (!email || !password) { return c.text("Invalid email or password", 400) } checkUserPassword(password); if (settings.enableMailVerify && !code) { return c.text("Need verify code", 400) } // check mail domain allow list const mailDomain = email.split("@")[1]; if (settings.enableMailAllowList && !settings.mailAllowList.includes(mailDomain) ) { return c.text(`Mail domain must in ${JSON.stringify(settings.mailAllowList, null, 2)}`, 400) } // check code if (settings.enableMailVerify) { const verifyCode = await c.env.KV.get(`temp-mail:${email}`) if (verifyCode != code) { return c.text("Invalid verify code", 400) } } // geo data const reqIp = c.req.raw.headers.get("cf-connecting-ip") const geoData = new GeoData(reqIp, c.req.raw.cf); const userInfo = new UserInfo(geoData); // if not enable mail verify, do not on conflict update if (!settings.enableMailVerify) { try { const { success } = await c.env.DB.prepare( `INSERT INTO users (user_email, password, user_info)` + ` VALUES (?, ?, ?)` ).bind( email, password, JSON.stringify(userInfo) ).run(); if (!success) { return c.text("Failed to register", 500) } } catch (e) { if (e.message && e.message.includes("UNIQUE")) { return c.text("User already exists, please login", 400) } return c.text(`Failed to register: ${e.message}`, 500) } return c.json({ success: true }) } // if enable mail verify, on conflict update const { success } = await c.env.DB.prepare( `INSERT INTO users (user_email, password, user_info)` + ` VALUES (?, ?, ?)` + ` ON CONFLICT(user_email) DO UPDATE SET password = ?, user_info = ?, updated_at = datetime('now')` ).bind( email, password, JSON.stringify(userInfo), password, JSON.stringify(userInfo) ).run(); if (!success) { return c.text("Failed to register", 500) } return c.json({ success: true }) }, login: async (c) => { const { email, password } = await c.req.json(); if (!email || !password) return c.text("Invalid email or password", 400); const { id: user_id, password: dbPassword } = await c.env.DB.prepare( `SELECT id, password FROM users where user_email = ?` ).bind(email).first() || {}; if (!dbPassword) { return c.text("User not found", 400) } // TODO: need check password use random salt if (dbPassword != password) { return c.text("Invalid password", 400) } // create jwt const jwt = await Jwt.sign({ user_email: email, user_id: user_id, // 30 days expire in seconds exp: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, iat: Math.floor(Date.now() / 1000), }, c.env.JWT_SECRET, "HS256") return c.json({ jwt: jwt }) }, }