新增规则触发人机验证和加载失败提示

This commit is contained in:
eoao
2025-07-29 20:00:00 +08:00
parent 72bcc7367b
commit cbefc1d6f2
25 changed files with 632 additions and 273 deletions
+5 -1
View File
@@ -255,7 +255,11 @@ const en = {
backgroundUrlDesc: 'Image URL',
localUpload: ' Local upload',
imageLink: 'Image URL',
backgroundWarning: 'Image file size affects website load speed.'
imageLinkErrorMsg: 'Invalid image URL',
backgroundWarning: 'Image file size affects website load speed.',
rulesVerify: 'Rules',
rulesVerifyTitle: 'Trigger After {count} Daily Uses per IP',
botVerifyMsg: 'Please verify that you are human',
}
export default en
+2 -2
View File
@@ -4,8 +4,8 @@ import zh from './zh.js'
const i18n = createI18n({
legacy: false,
messages: {
en,
zh
zh,
en
},
});
+4
View File
@@ -255,6 +255,10 @@ const zh = {
backgroundUrlDesc: '在线图片链接',
localUpload: '本地上传',
imageLink: '图片链接',
imageLinkErrorMsg: '图片链接不正确',
backgroundWarning: '图片文件大小会影响网站加载速度',
rulesVerify: '规则',
rulesVerifyTitle: 'IP 每天使用 {count} 次后触发',
botVerifyMsg: '请完成人机验证',
}
export default zh
+3 -1
View File
@@ -6,6 +6,7 @@ import {permsToRouter} from "@/perm/perm.js";
import router from "@/router";
import {websiteConfig} from "@/request/setting.js";
import {cvtR2Url} from "@/utils/convert.js";
import i18n from "@/i18n/index.js";
export async function init() {
document.title = '\u200B'
@@ -15,11 +16,12 @@ export async function init() {
const accountStore = useAccountStore();
const token = localStorage.getItem('token');
if (!settingStore.lang) {
settingStore.lang = navigator.language.split('-')[0]
}
i18n.global.locale.value = settingStore.lang
let setting = null;
if (token) {
+44 -11
View File
@@ -103,7 +103,10 @@
:class="verifyShow ? 'turnstile-show' : 'turnstile-hide'"
:data-sitekey="settingStore.settings.siteKey"
data-callback="onTurnstileSuccess"
></div>
data-error-callback="onTurnstileError"
>
<span style="font-size: 12px;color: #F56C6C" v-if="botJsError">人机验证模块加载失败,请刷新浏览器</span>
</div>
</el-dialog>
<el-dialog v-model="setNameShow" :title="$t('changeUserName')" >
<div class="container">
@@ -145,6 +148,7 @@ const accountName = ref(null)
const addRef = ref({})
let account = null
let turnstileId = null
const botJsError = ref(false)
let verifyToken = ''
const addForm = reactive({
email: '',
@@ -170,11 +174,19 @@ const openSelect = () => {
mySelect.value.toggleMenu()
}
window.onTurnstileError = (e) => {
console.log('人机验加载失败')
nextTick(() => {
if (!turnstileId) {
turnstileId = window.turnstile.render('.register-turnstile')
} else {
window.turnstile.reset(turnstileId);
}
})
};
window.onTurnstileSuccess = (token) => {
verifyToken = token;
setTimeout(() => {
verifyShow.value = false
},1500)
};
function setName() {
@@ -338,14 +350,27 @@ function submit() {
})
return
}
if (!verifyToken && settingStore.settings.addEmailVerify === 0) {
verifyShow.value = true
if (!turnstileId) {
if (!verifyToken && (settingStore.settings.addEmailVerify === 0 || (settingStore.settings.addEmailVerify === 2 && settingStore.settings.addVerifyOpen))) {
if (!verifyShow.value) {
verifyShow.value = true
nextTick(() => {
turnstileId = window.turnstile.render('.add-email-turnstile')
if (!turnstileId) {
try {
turnstileId = window.turnstile.render('.add-email-turnstile')
} catch (e) {
botJsError.value = true
console.log('人机验证js加载失败')
}
} else {
window.turnstile.reset('.add-email-turnstile')
}
})
} else if (!botJsError.value) {
ElMessage({
message: t('botVerifyMsg'),
type: "error",
plain: true
})
} else {
window.turnstile.reset(turnstileId)
}
return;
}
@@ -357,16 +382,24 @@ function submit() {
addForm.email = ''
accounts.push(account)
verifyToken = ''
settingStore.settings.addVerifyOpen = account.addVerifyOpen
ElMessage({
message: t('addSuccessMsg'),
type: "success",
plain: true
})
verifyShow.value = false
userStore.refreshUserInfo()
}).catch(res => {
if (res.code === 400) {
verifyToken = ''
window.turnstile.reset(turnstileId)
if (turnstileId) {
window.turnstile.reset(turnstileId)
} else {
nextTick(() => {
turnstileId = window.turnstile.render('.add-email-turnstile')
})
}
verifyShow.value = true
}
addLoading.value = false
+62 -19
View File
@@ -13,11 +13,12 @@
<span class="form-title">{{settingStore.settings.title}}</span>
<span class="form-desc" v-if="show === 'login'">{{$t('loginTitle')}}</span>
<span class="form-desc" v-else>{{$t('regTitle')}}</span>
<div v-if="show === 'login'">
<div v-show="show === 'login'">
<el-input class="email-input" v-model="form.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
v-if="show === 'login'"
ref="mySelect"
v-model="suffix"
:placeholder="$t('select')"
@@ -43,11 +44,12 @@
>{{$t('loginBtn')}}
</el-button>
</div>
<div v-else>
<div v-show="show !== 'login'">
<el-input class="email-input" v-model="registerForm.email" type="text" :placeholder="$t('emailAccount')" autocomplete="off">
<template #append>
<div @click.stop="openSelect">
<el-select
v-if="show !== 'login'"
ref="mySelect"
v-model="suffix"
:placeholder="$t('select')"
@@ -75,7 +77,12 @@
class="register-turnstile"
:data-sitekey="settingStore.settings.siteKey"
data-callback="onTurnstileSuccess"
></div>
data-error-callback="onTurnstileError"
data-after-interactive-callback="loadAfter"
data-before-interactive-callback="loadBefore"
>
<span style="font-size: 12px;color: #F56C6C" v-if="botJsError">人机验证模块加载失败,请刷新浏览器</span>
</div>
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
>{{$t('regBtn')}}
</el-button>
@@ -129,15 +136,30 @@ suffix.value = domainList[0]
const verifyShow = ref(false)
let verifyToken = ''
let turnstileId = null
let botJsError = ref(false)
window.onTurnstileSuccess = (token) => {
verifyToken = token;
setTimeout(() => {
verifyShow.value = false
}, 2000)
};
window.onTurnstileError = (e) => {
console.log('人机验加载失败')
nextTick(() => {
if (!turnstileId) {
turnstileId = window.turnstile.render('.register-turnstile')
} else {
window.turnstile.reset(turnstileId);
}
})
};
window.loadAfter = (e) => {
console.log('loadAfter')
}
window.loadBefore = (e) => {
console.log('loadBefore')
}
const loginOpacity = computed(() => {
return `rgba(255, 255, 255, ${settingStore.settings.loginOpacity})`
@@ -158,7 +180,6 @@ const openSelect = () => {
mySelect.value.toggleMenu()
}
const submit = () => {
if (!form.email) {
@@ -267,15 +288,26 @@ function submitRegister() {
}
if (!verifyToken && settingStore.settings.registerVerify === 0) {
verifyShow.value = true
if (!turnstileId) {
if (!verifyToken && (settingStore.settings.registerVerify === 0 || (settingStore.settings.registerVerify === 2 && settingStore.settings.regVerifyOpen))) {
if (!verifyShow.value) {
verifyShow.value = true
nextTick(() => {
turnstileId = window.turnstile.render('.register-turnstile')
if (!turnstileId) {
try {
turnstileId = window.turnstile.render('.register-turnstile')
} catch (e) {
botJsError.value = true
console.log('人机验证js加载失败')
}
} else {
window.turnstile.reset('.register-turnstile')
}
})
} else {
nextTick(() => {
window.turnstile.reset(turnstileId);
} else if (!botJsError.value) {
ElMessage({
message: t('botVerifyMsg'),
type: "error",
plain: true
})
}
return;
@@ -290,27 +322,38 @@ function submitRegister() {
code: registerForm.code
}
register(form).then(() => {
register(form).then(({regVerifyOpen}) => {
show.value = 'login'
registerForm.email = ''
registerForm.password = ''
registerForm.confirmPassword = ''
registerForm.code = ''
registerLoading.value = false
turnstileId = null
verifyToken = ''
settingStore.settings.regVerifyOpen = regVerifyOpen
verifyShow.value = false
ElMessage({
message: t('regSuccessMsg'),
type: 'success',
plain: true,
})
}).catch(res => {
registerLoading.value = false
if (res.code === 400) {
verifyToken = ''
window.turnstile.reset(turnstileId)
settingStore.settings.regVerifyOpen = true
if (turnstileId) {
window.turnstile.reset(turnstileId)
} else {
nextTick(() => {
turnstileId = window.turnstile.render('.register-turnstile')
})
}
verifyShow.value = true
}
registerLoading.value = false
});
}
+78 -8
View File
@@ -230,15 +230,39 @@
<div class="setting-item">
<div><span>{{$t('signUpVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.registerVerify"/>
<el-button class="opt-button" size="small" type="primary" @click="openRegVerifyCount">
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
</el-button>
<el-select
@change="change"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.registerVerify"
placeholder="Select"
class="bot-verify-select"
>
<el-option key="1" :value="0" :label="$t('enable')" />
<el-option key="1" :value="1" :label="$t('disable')" />
<el-option key="1" :value="2" :label="$t('rulesVerify')" />
</el-select>
</div>
</div>
<div class="setting-item">
<div><span>{{$t('addEmailVerification')}}</span></div>
<div>
<el-switch @change="change" :before-change="beforeChange" :active-value="0" :inactive-value="1"
v-model="setting.addEmailVerify"/>
<el-button class="opt-button" size="small" type="primary" @click="openAddVerifyCount">
<Icon icon="fluent:settings-48-regular" width="18" height="18"/>
</el-button>
<el-select
@change="change"
:style="`width: ${ locale === 'en' ? 100 : 80 }px;`"
v-model="setting.addEmailVerify"
placeholder="Select"
class="bot-verify-select"
>
<el-option key="1" :value="0" :label="$t('enable')" />
<el-option key="1" :value="1" :label="$t('disable')" />
<el-option key="1" :value="2" :label="$t('rulesVerify')" />
</el-select>
</div>
</div>
<div class="setting-item">
@@ -450,6 +474,19 @@
<el-table-column :width="tokenColumnWidth" property="value" label="Token" fixed="right" :show-overflow-tooltip="true" />
</el-table>
</el-dialog>
<el-dialog v-model="showRegVerifyCount" :title="$t('rulesVerifyTitle',{count: regVerifyCount})" @closed="regVerifyCount = setting.regVerifyCount" >
<form>
<el-input-number type="text" v-model="regVerifyCount" :min="1" >
</el-input-number>
<el-button type="primary" :loading="settingLoading" @click="saveRegVerifyCount">{{$t('save')}}</el-button>
</form>
</el-dialog>
<el-dialog v-model="showAddVerifyCount" :title="$t('rulesVerifyTitle',{count: addVerifyCount})" @closed="addVerifyCount = setting.addVerifyCount">
<form>
<el-input-number type="text" v-model="addVerifyCount" :min="1"/>
<el-button type="primary" :loading="settingLoading" @click="saveAddVerifyCount">{{$t('save')}}</el-button>
</form>
</el-dialog>
</el-scrollbar>
</div>
</template>
@@ -497,7 +534,11 @@ const loginOpacity = ref(0)
const backgroundUrl = ref('')
let backgroundFile = {}
const showSetBackground = ref(false)
let regVerifyCount = ref(1)
let addVerifyCount = ref(1)
let backup = '{}'
const showAddVerifyCount = ref(false)
const showRegVerifyCount = ref(false)
const resendTokenForm = reactive({
domain: '',
token: '',
@@ -542,8 +583,20 @@ settingQuery().then(settingData => {
backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : ''
editTitle.value = setting.value.title
r2DomainInput.value = setting.value.r2Domain
addVerifyCount.value = setting.value.addVerifyCount
regVerifyCount.value = setting.value.regVerifyCount
})
function openAddVerifyCount() {
if (settingLoading.value) return
showAddVerifyCount.value = true
}
function openRegVerifyCount() {
if (settingLoading.value) return
showRegVerifyCount.value = true
}
const resendList = computed(() => {
let list = Object.keys(setting.value.resendTokens).map(key => {
@@ -566,6 +619,20 @@ const resendList = computed(() => {
return list;
});
function saveAddVerifyCount() {
if(!addVerifyCount.value) {
addVerifyCount.value = 1
}
editSetting({addVerifyCount: addVerifyCount.value})
}
function saveRegVerifyCount() {
if (!regVerifyCount.value) {
regVerifyCount.value = 1
}
editSetting({regVerifyCount: regVerifyCount.value})
}
const compareByLengthAndUpperCase = (a, b, key) => {
const getUpperCaseCount = (str) => (str.match(/[A-Z]/g) || []).length;
if (a[key].length === b[key].length) {
@@ -742,9 +809,9 @@ async function saveBackground() {
if (localUpShow.value) {
image = await fileToBase64(backgroundFile,true);
} else {
if (!backgroundUrl.value.startsWith('http')) {
if (backgroundUrl.value && !backgroundUrl.value.startsWith('http')) {
ElMessage({
message: '图片地址不正确',
message: t('imageLinkErrorMsg'),
type: "error",
plain: true
})
@@ -872,8 +939,9 @@ function editSetting(settingForm, refreshStatus = true) {
tgSettingShow.value = false
thirdEmailShow.value = false
forwardRulesShow.value = false
showAddVerifyCount.value = false
showRegVerifyCount.value = false
}).catch((e) => {
console.error(e)
loginOpacity.value = setting.value.loginOpacity
setting.value = {...setting.value, ...JSON.parse(backup)}
}).finally(() => {
@@ -941,7 +1009,9 @@ function editSetting(settingForm, refreshStatus = true) {
flex-direction: column;
}
.bot-verify-select {
margin-left: 10px;
}
.settings-card {
background-color: #fff;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -6,8 +6,8 @@
<title></title>
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script type="module" crossorigin src="/assets/index-BOcWPX50.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CTj27R8v.css">
<script type="module" crossorigin src="/assets/index-BwB6muO3.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BEDjT-8v.css">
</head>
<body>
<div id="loading-first">
+1
View File
@@ -1 +1,2 @@
import app from '../hono/hono';
import result from '../model/result';
+10 -1
View File
@@ -1,3 +1,5 @@
import verifyRecordService from '../service/verify-record-service';
export const userConst = {
status: {
NORMAL: 0,
@@ -74,7 +76,7 @@ export const settingConst = {
},
addEmail: {
OPEN: 0,
CLOSE: 1,
CLOSE: 1
},
manyEmail: {
OPEN: 0,
@@ -83,10 +85,12 @@ export const settingConst = {
registerVerify: {
OPEN: 0,
CLOSE: 1,
COUNT: 2,
},
addEmailVerify: {
OPEN: 0,
CLOSE: 1,
COUNT: 2,
},
forwardStatus: {
OPEN: 0,
@@ -102,6 +106,11 @@ export const settingConst = {
}
}
export const verifyRecordType = {
REG: 0,
ADD: 1,
}
export const isDel = {
DELETE: 1,
+2
View File
@@ -8,6 +8,8 @@ export const setting = sqliteTable('setting', {
autoRefreshTime: integer('auto_refresh_time').default(0).notNull(),
addEmailVerify: integer('add_email_verify').default(1).notNull(),
registerVerify: integer('register_verify').default(1).notNull(),
regVerifyCount: integer('reg_verify_count').default(1).notNull(),
addVerifyCount: integer('add_verify_count').default(1).notNull(),
send: integer('send').default(1).notNull(),
r2Domain: text('r2_domain'),
secretKey: text('secret_key'),
+10
View File
@@ -0,0 +1,10 @@
import { sqliteTable, text, integer} from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';
export const email = sqliteTable('verify_record', {
vrId: integer('vr_id').primaryKey({ autoIncrement: true }),
ip: integer('ip').notNull().default(''),
count: integer('count').notNull().default(1),
type: integer('type').notNull().default(0),
updateTime: text('update_time').default(sql`CURRENT_TIMESTAMP`).notNull(),
});
export default email
+1 -1
View File
@@ -48,7 +48,7 @@ const en = {
noOsUpBack: 'Cannot upload background: R2 object storage not configured',
noOsDomainUpBack: 'Cannot upload background: R2 domain not configured',
starNotExistEmail: 'Starred email does not exist',
emptyBotToken: 'Verification token cannot be empty',
emptyBotToken: 'Please verify that you are human',
botVerifyFail: 'Bot verification failed, please try again',
authExpired: 'Authentication has expired. Please sign in again',
unauthorized: 'Unauthorized',
+1 -1
View File
@@ -48,7 +48,7 @@ const zh = {
noOsUpBack: 'r2对象存储未配置不能上传背景',
noOsDomainUpBack: 'r2域名未配置不能上传背景',
starNotExistEmail: '星标的邮件不存在',
emptyBotToken: '验证token不能为空',
emptyBotToken: '需要进行人机验证',
botVerifyFail: '人机验证失败,请重试',
authExpired: '身份认证失效,请重新登录',
unauthorized: '权限不足',
+3
View File
@@ -1,6 +1,8 @@
import app from './hono/webs';
import { email } from './email/email';
import userService from './service/user-service';
import verifyRecord from './entity/verify-record';
import verifyRecordService from './service/verify-record-service';
export default {
async fetch(req, env, ctx) {
const url = new URL(req.url)
@@ -16,6 +18,7 @@ export default {
},
email: email,
async scheduled(c, env, ctx) {
await verifyRecordService.clearRecord({env})
await userService.resetDaySendCount({ env })
},
};
+24
View File
@@ -18,10 +18,34 @@ const init = {
await this.v1_3_1DB(c);
await this.v1_4DB(c);
await this.v1_5DB(c);
await this.v1_6DB(c);
await settingService.refresh(c);
return c.text(t('initSuccess'));
},
async v1_6DB(c) {
const ADD_COLUMN_SQL_LIST = [
`ALTER TABLE setting ADD COLUMN reg_verify_count INTEGER NOT NULL DEFAULT 1;`,
`ALTER TABLE setting ADD COLUMN add_verify_count INTEGER NOT NULL DEFAULT 1;`,
`CREATE TABLE IF NOT EXISTS verify_record (
vr_id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL DEFAULT '',
count INTEGER NOT NULL DEFAULT 1,
type INTEGER NOT NULL DEFAULT 0,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP
)`
];
for (let sql of ADD_COLUMN_SQL_LIST) {
try {
await c.env.db.prepare(sql).run();
} catch (e) {
console.warn(`跳过字段添加,原因:${e.message}`);
}
}
},
async v1_5DB(c) {
await c.env.db.prepare(`UPDATE perm SET perm_key = 'sys-email:list' WHERE perm_key = 'all-email:list'`).run();
await c.env.db.prepare(`UPDATE perm SET perm_key = 'sys-email:delete' WHERE perm_key = 'all-email:delete'`).run();
+30 -6
View File
@@ -6,21 +6,26 @@ import emailService from './email-service';
import orm from '../entity/orm';
import account from '../entity/account';
import { and, asc, eq, gt, inArray, count, sql } from 'drizzle-orm';
import { isDel } from '../const/entity-const';
import { isDel, settingConst } from '../const/entity-const';
import settingService from './setting-service';
import turnstileService from './turnstile-service';
import roleService from './role-service';
import { t } from '../i18n/i18n';
import verifyRecordService from './verify-record-service';
const accountService = {
async add(c, params, userId) {
if (!await settingService.isAddEmail(c)) {
const {addEmailVerify , addEmail, manyEmail, addVerifyCount} = await settingService.query(c);
let { email, token } = params;
if (!(addEmail === settingConst.addEmail.OPEN && manyEmail === settingConst.manyEmail.OPEN)) {
throw new BizError(t('addAccountDisabled'));
}
let { email, token } = params;
if (!email) {
throw new BizError(t('emptyEmail'));
@@ -35,7 +40,7 @@ const accountService = {
}
const accountRow = await this.selectByEmailIncludeDelNoCase(c, email);
let accountRow = await this.selectByEmailIncludeDelNoCase(c, email);
if (accountRow && accountRow.isDel === isDel.DELETE) {
throw new BizError(t('isDelAccount'));
@@ -61,11 +66,30 @@ const accountService = {
}
if (await settingService.isAddEmailVerify(c)) {
let addVerifyOpen = false
if (addEmailVerify === settingConst.addEmailVerify.OPEN) {
addVerifyOpen = true
await turnstileService.verify(c, token);
}
return orm(c).insert(account).values({ email: email, userId: userId, name: emailUtils.getName(email) }).returning().get();
if (addEmailVerify === settingConst.addEmailVerify.COUNT) {
addVerifyOpen = await verifyRecordService.isOpenAddVerify(c, addVerifyCount);
if (addVerifyOpen) {
await turnstileService.verify(c,token)
}
}
accountRow = await orm(c).insert(account).values({ email: email, userId: userId, name: emailUtils.getName(email) }).returning().get();
if (addEmailVerify === settingConst.addEmailVerify.COUNT && !addVerifyOpen) {
const row = await verifyRecordService.increaseAddCount(c);
addVerifyOpen = row.count >= addVerifyCount
}
accountRow.addVerifyOpen = addVerifyOpen
return accountRow;
},
selectByEmailIncludeDelNoCase(c, email) {
+25 -5
View File
@@ -18,6 +18,7 @@ import regKeyService from './reg-key-service';
import dayjs from 'dayjs';
import { toUtc } from '../utils/date-uitil';
import { t } from '../i18n/i18n.js';
import verifyRecordService from './verify-record-service';
const loginService = {
@@ -25,11 +26,8 @@ const loginService = {
const { email, password, token, code } = params;
const {regKey, register, registerVerify} = await settingService.query(c)
const {regKey, register, registerVerify, regVerifyCount} = await settingService.query(c)
if (registerVerify === settingConst.registerVerify.OPEN) {
await turnstileService.verify(c,token)
}
if (register === settingConst.register.CLOSE) {
throw new BizError(t('regDisabled'));
@@ -80,7 +78,6 @@ const loginService = {
throw new BizError(t('isRegAccount'));
}
const { salt, hash } = await saltHashUtils.hashPassword(password);
let defType = null
@@ -104,6 +101,22 @@ const loginService = {
}
let regVerifyOpen = false
if (registerVerify === settingConst.registerVerify.OPEN) {
regVerifyOpen = true
await turnstileService.verify(c,token)
}
if (registerVerify === settingConst.registerVerify.COUNT) {
regVerifyOpen = await verifyRecordService.isOpenRegVerify(c, regVerifyCount);
if (regVerifyOpen) {
await turnstileService.verify(c,token)
}
}
const { salt, hash } = await saltHashUtils.hashPassword(password);
const userId = await userService.insert(c, { email, regKeyId,password: hash, salt, type: type || defType });
await userService.updateUserInfo(c, userId, true);
@@ -114,6 +127,13 @@ const loginService = {
await regKeyService.reduceCount(c, code, 1);
}
if (registerVerify === settingConst.registerVerify.COUNT && !regVerifyOpen) {
const row = await verifyRecordService.increaseRegCount(c);
return {regVerifyOpen: row.count >= regVerifyCount}
}
return {regVerifyOpen}
},
async handleOpenRegKey(c, regKey, code) {
+31 -20
View File
@@ -1,7 +1,7 @@
import KvConst from '../const/kv-const';
import setting from '../entity/setting';
import orm from '../entity/orm';
import { settingConst } from '../const/entity-const';
import { settingConst, verifyRecordType } from '../const/entity-const';
import fileUtils from '../utils/file-utils';
import r2Service from './r2-service';
import emailService from './email-service';
@@ -10,6 +10,7 @@ import userService from './user-service';
import constant from '../const/constant';
import BizError from '../error/biz-error';
import { t } from '../i18n/i18n'
import verifyRecordService from './verify-record-service';
const settingService = {
@@ -31,11 +32,32 @@ const settingService = {
},
async get(c) {
const settingRow = await this.query(c);
const [settingRow, recordList] = await Promise.all([
await this.query(c),
verifyRecordService.selectListByIP(c)
]);
settingRow.secretKey = settingRow.secretKey ? `${settingRow.secretKey.slice(0, 12)}******` : null;
Object.keys(settingRow.resendTokens).forEach(key => {
settingRow.resendTokens[key] = `${settingRow.resendTokens[key].slice(0, 12)}******`;
});
let regVerifyOpen = false
let addVerifyOpen = false
recordList.forEach(row => {
if (row.type === verifyRecordType.REG) {
regVerifyOpen = row.count >= settingRow.regVerifyCount
}
if (row.type === verifyRecordType.ADD) {
addVerifyOpen = row.count >= settingRow.addVerifyCount
}
})
settingRow.regVerifyOpen = regVerifyOpen
settingRow.addVerifyOpen = addVerifyOpen
return settingRow;
},
@@ -50,28 +72,13 @@ const settingService = {
await this.refresh(c);
},
async isAddEmail(c) {
const { addEmail, manyEmail } = await this.query(c);
return addEmail === settingConst.addEmail.OPEN && manyEmail === settingConst.manyEmail.OPEN;
},
async isRegisterVerify(c) {
const { registerVerify } = await this.query(c);
return registerVerify === settingConst.registerVerify.OPEN;
},
async isAddEmailVerify(c) {
const { addEmailVerify } = await this.query(c);
return addEmailVerify === settingConst.addEmailVerify.OPEN;
},
async setBackground(c, params) {
const settingRow = await this.query(c);
let { background } = params
if (!background.startsWith('http')) {
if (background && !background.startsWith('http')) {
if (!c.env.r2) {
throw new BizError(t('noOsUpBack'));
@@ -113,7 +120,9 @@ const settingService = {
},
async websiteConfig(c) {
const settingRow = await this.get(c);
const settingRow = await this.get(c)
return {
register: settingRow.register,
title: settingRow.title,
@@ -128,7 +137,9 @@ const settingService = {
background: settingRow.background,
loginOpacity: settingRow.loginOpacity,
domainList:settingRow.domainList,
regKey: settingRow.regKey
regKey: settingRow.regKey,
regVerifyOpen: settingRow.regVerifyOpen,
addVerifyOpen: settingRow.addVerifyOpen
};
}
};
+1 -1
View File
@@ -7,7 +7,7 @@ const turnstileService = {
async verify(c, token) {
if (!token) {
throw new BizError(t('emptyBotToken'));
throw new BizError(t('emptyBotToken'),400);
}
const settingRow = await settingService.query(c)
@@ -0,0 +1,90 @@
import orm from '../entity/orm';
import verifyRecord from '../entity/verify-record';
import { eq, sql, and } from 'drizzle-orm';
import dayjs from 'dayjs';
import ipUtils from '../utils/ip-utils';
import { verifyRecordType } from '../const/entity-const';
const verifyRecordService = {
async selectListByIP(c) {
const ip = ipUtils.getIp(c)
return orm(c).select().from(verifyRecord).where(eq(verifyRecord.ip, ip)).all();
},
async clearRecord(c) {
await orm(c).delete(verifyRecord).run();
},
async isOpenRegVerify(c, regVerifyCount) {
const ip = ipUtils.getIp(c)
const row = await orm(c).select().from(verifyRecord).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.REG))).get();
if (row) {
if (row.count >= regVerifyCount){
return true
}
}
return false
},
async isOpenAddVerify(c, addVerifyCount) {
const ip = ipUtils.getIp(c)
const row = await orm(c).select().from(verifyRecord).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.ADD))).get();
if (row) {
if (row.count >= addVerifyCount){
return true
}
}
return false
},
async increaseRegCount(c) {
const ip = ipUtils.getIp(c)
const row = await orm(c).select().from(verifyRecord).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.REG))).get();
const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
if (row) {
return orm(c).update(verifyRecord).set({
count: sql`${verifyRecord.count}
+ 1`, updateTime: now
}).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.REG))).returning().get();
} else {
return orm(c).insert(verifyRecord).values({ip, type: verifyRecordType.REG}).returning().run();
}
},
async increaseAddCount(c) {
const ip = ipUtils.getIp(c)
const row = await orm(c).select().from(verifyRecord).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.ADD))).get();
const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
if (row) {
return orm(c).update(verifyRecord).set({
count: sql`${verifyRecord.count}
+ 1`, updateTime: now
}).where(and(eq(verifyRecord.ip, ip),eq(verifyRecord.type,verifyRecordType.ADD))).returning().get();
} else {
return orm(c).insert(verifyRecord).values({ip, type: verifyRecordType.ADD}).returning().get();
}
}
};
export default verifyRecordService;
+9
View File
@@ -0,0 +1,9 @@
const ipUtils = {
getIp(c) {
return c.req.header('CF-Connecting-IP') ||
c.req.header('X-Forwarded-For') ||
'Unknown';
}
}
export default ipUtils