feat: add NO_LIMIT_SEND_ROLE (#373)

This commit is contained in:
Dream Hunter
2024-08-04 21:02:11 +08:00
committed by GitHub
parent 979b6eae1a
commit 9246550cc5
10 changed files with 54 additions and 18 deletions

View File

@@ -5,7 +5,8 @@
- Docs: Update new-address-api.md (#360)
- feat: worker 增加 `ADMIN_USER_ROLE` 配置, 用于配置管理员用户角色,此角色的用户可访问 admin 管理页面 (#363)
- feat: worker 增加 `SHOW_GITHUB` 配置, 用于配置是否显示 github 链接
- feat: worker 增加 `DISABLE_SHOW_GITHUB` 配置, 用于配置是否显示 github 链接
- feat: worker 增加 `NO_LIMIT_SEND_ROLE` 配置, 用于配置可以无限发送邮件的角色
## v0.6.1

View File

@@ -25,7 +25,7 @@ export const useGlobalState = createGlobalState(
cfTurnstileSiteKey: '',
enableWebhook: false,
isS3Enabled: false,
showGithub: false,
showGithub: true,
})
const settings = ref({
fetched: false,

View File

@@ -109,9 +109,10 @@ ENABLE_AUTO_REPLY = false
# ENABLE_WEBHOOK = true
# Footer text
# COPYRIGHT = "Dream Hunter"
# SHOW_GITHUB = true # Show GitHub link
# DISABLE_SHOW_GITHUB = true # Disable Show GitHub link
# default send balance, if not set, it will be 0
# DEFAULT_SEND_BALANCE = 1
# NO_LIMIT_SEND_ROLE = "vip" # the role which can send emails without limit
# Turnstile verification configuration
# CF_TURNSTILE_SITE_KEY = ""
# CF_TURNSTILE_SECRET_KEY = ""

View File

@@ -80,9 +80,10 @@ ENABLE_AUTO_REPLY = false
# ENABLE_WEBHOOK = true
# 前端界面页脚文本
# COPYRIGHT = "Dream Hunter"
# SHOW_GITHUB = true # 是否显示 GitHub 链接
# DISABLE_SHOW_GITHUB = true # 是否显示 GitHub 链接
# 默认发送邮件余额,如果不设置,将为 0
# DEFAULT_SEND_BALANCE = 1
# NO_LIMIT_SEND_ROLE = "vip" # 可以无限发送邮件的角色
# Turnstile 人机验证配置
# CF_TURNSTILE_SITE_KEY = ""
# CF_TURNSTILE_SECRET_KEY = ""

View File

@@ -35,7 +35,7 @@ api.get('/open_api/settings', async (c) => {
"enableWebhook": getBooleanValue(c.env.ENABLE_WEBHOOK),
"isS3Enabled": isS3Enabled(c),
"version": CONSTANTS.VERSION,
"showGithub": getBooleanValue(c.env.SHOW_GITHUB),
"showGithub": !getBooleanValue(c.env.DISABLE_SHOW_GITHUB),
});
})

View File

@@ -1,7 +1,7 @@
import { Hono } from 'hono'
import { HonoCustomType } from "../types";
import { getBooleanValue, getJsonSetting, checkCfTurnstile } from '../utils';
import { getBooleanValue, getJsonSetting, checkCfTurnstile, getStringValue } from '../utils';
import { newAddress, handleListQuery, deleteAddressWithData, getAddressPrefix, getAllowDomains } from '../common'
import { CONSTANTS } from '../constants'
import auto_reply from './auto_reply'
@@ -49,6 +49,7 @@ api.delete('/api/mails/:id', async (c) => {
api.get('/api/settings', async (c) => {
const { address, address_id } = c.get("jwtPayload")
const user_role = c.get("userRolePayload")
if (address_id && address_id > 0) {
try {
const db_address_id = await c.env.DB.prepare(
@@ -82,7 +83,8 @@ api.get('/api/settings', async (c) => {
} catch (e) {
console.warn("Failed to update address")
}
const balance = await c.env.DB.prepare(
const is_no_limit_send_balance = user_role && user_role === getStringValue(c.env.NO_LIMIT_SEND_ROLE);
const balance = is_no_limit_send_balance ? 99999 : await c.env.DB.prepare(
`SELECT balance FROM address_sender where address = ? and enabled = 1`
).bind(address).first("balance");
return c.json({

View File

@@ -4,7 +4,7 @@ import { createMimeMessage } from 'mimetext';
import { Resend } from 'resend';
import { CONSTANTS } from '../constants'
import { getJsonSetting, getDomains, getIntValue, getBooleanValue } from '../utils';
import { getJsonSetting, getDomains, getIntValue, getBooleanValue, getStringValue } from '../utils';
import { GeoData } from '../models'
import { handleListQuery } from '../common'
import { HonoCustomType } from '../types';
@@ -105,13 +105,17 @@ export const sendMail = async (
if (!domains.includes(mailDomain)) {
throw new Error("Invalid domain")
}
// check permission
const balance = await c.env.DB.prepare(
`SELECT balance FROM address_sender
const user_role = c.get("userRolePayload");
const is_no_limit_send_balance = user_role && user_role === getStringValue(c.env.NO_LIMIT_SEND_ROLE);
if (!is_no_limit_send_balance) {
// check permission
const balance = await c.env.DB.prepare(
`SELECT balance FROM address_sender
where address = ? and enabled = 1`
).bind(address).first<number>("balance");
if (!balance || balance <= 0) {
throw new Error("No balance")
).bind(address).first<number>("balance");
if (!balance || balance <= 0) {
throw new Error("No balance")
}
}
const {
from_name, to_mail, to_name,
@@ -154,7 +158,7 @@ export const sendMail = async (
throw new Error("Please enable resend or verified address list")
}
// update balance
if (!sendByVerifiedAddressList) {
if (!sendByVerifiedAddressList && !is_no_limit_send_balance) {
try {
const { success } = await c.env.DB.prepare(
`UPDATE address_sender SET balance = balance - 1 where address = ?`

View File

@@ -33,9 +33,10 @@ export type Bindings = {
ENABLE_USER_DELETE_EMAIL: string | boolean | undefined
ENABLE_INDEX_ABOUT: string | boolean | undefined
DEFAULT_SEND_BALANCE: number | string | undefined
NO_LIMIT_SEND_ROLE: string | undefined | null
ADMIN_CONTACT: string | undefined
COPYRIGHT: string | undefined
SHOW_GITHUB: string | boolean | undefined
DISABLE_SHOW_GITHUB: string | boolean | undefined
FORWARD_ADDRESS_LIST: string | string[] | undefined
// s3 config
@@ -72,7 +73,8 @@ type UserPayload = {
type Variables = {
userPayload: UserPayload,
jwtPayload: JwtPayload
userRolePayload: string | undefined | null,
jwtPayload: JwtPayload,
}
type HonoCustomType = {

View File

@@ -75,6 +75,26 @@ const checkUserPayload = async (
}
}
const checkoutUserRolePayload = async (
c: Context<HonoCustomType>
): Promise<void> => {
try {
const token = c.req.raw.headers.get("x-user-access-token");
if (!token) return;
const payload = await Jwt.verify(token, c.env.JWT_SECRET, "HS256");
// check expired
if (!payload.exp) return;
// exp is in seconds
if (payload.exp < Math.floor(Date.now() / 1000)) {
return;
}
if (typeof payload?.user_role !== "string") return;
c.set("userRolePayload", payload.user_role);
} catch (e) {
console.error(e);
}
}
// api auth
app.use('/api/*', async (c, next) => {
// check header x-custom-auth
@@ -90,6 +110,11 @@ app.use('/api/*', async (c, next) => {
await next();
return;
}
if (c.req.path.startsWith("/api/settings")
|| c.req.path.startsWith("/api/send_mail")
) {
await checkoutUserRolePayload(c);
}
return jwt({ secret: c.env.JWT_SECRET, alg: "HS256" })(c, next);
});
// user_api auth

View File

@@ -51,7 +51,7 @@ ENABLE_AUTO_REPLY = false
# ENABLE_WEBHOOK = true
# Footer text
# COPYRIGHT = "Dream Hunter"
# SHOW_GITHUB = true
# DISABLE_SHOW_GITHUB = true
# default send balance, if not set, it will be 0
# DEFAULT_SEND_BALANCE = 1
# Turnstile verification