diff --git a/api/core/types/config.go b/api/core/types/config.go
index 10f16f62..07d19ff1 100644
--- a/api/core/types/config.go
+++ b/api/core/types/config.go
@@ -161,10 +161,10 @@ type SystemConfig struct {
SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词
MjMode string `json:"mj_mode"` // midjourney 默认的API模式,relax, fast, turbo
- IndexBgURL string `json:"index_bg_url"` // 前端首页背景图片
- IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单
- Copyright string `json:"copyright"` // 版权信息
- MarkMapText string `json:"mark_map_text,omitempty"` // 思维导入的默认文本
+ IndexBgURL string `json:"index_bg_url"` // 前端首页背景图片
+ IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单
+ Copyright string `json:"copyright"` // 版权信息
+ MarkMapText string `json:"mark_map_text"` // 思维导入的默认文本
- EnabledVerify bool `json:"enabled_verify,omitempty"` // 是否启用验证码
+ EnabledVerify bool `json:"enabled_verify"` // 是否启用验证码
}
diff --git a/api/handler/admin/admin_handler.go b/api/handler/admin/admin_handler.go
index eaaaf8fd..f4677723 100644
--- a/api/handler/admin/admin_handler.go
+++ b/api/handler/admin/admin_handler.go
@@ -14,6 +14,7 @@ import (
"geekai/core/types"
"geekai/handler"
logger2 "geekai/logger"
+ "geekai/service"
"geekai/store/model"
"geekai/store/vo"
"geekai/utils"
@@ -28,33 +29,49 @@ import (
var logger = logger2.GetLogger()
-// Manager 管理员
-type Manager struct {
- Username string `json:"username"`
- Password string `json:"password"`
- Captcha string `json:"captcha"` // 验证码
- CaptchaId string `json:"captcha_id"` // 验证码id
-}
-
const SuperManagerID = 1
type ManagerHandler struct {
handler.BaseHandler
- redis *redis.Client
+ redis *redis.Client
+ captcha *service.CaptchaService
}
-func NewAdminHandler(app *core.AppServer, db *gorm.DB, client *redis.Client) *ManagerHandler {
- return &ManagerHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}, redis: client}
+func NewAdminHandler(app *core.AppServer, db *gorm.DB, client *redis.Client, captcha *service.CaptchaService) *ManagerHandler {
+ return &ManagerHandler{
+ BaseHandler: handler.BaseHandler{DB: db, App: app},
+ redis: client,
+ captcha: captcha,
+ }
}
// Login 登录
func (h *ManagerHandler) Login(c *gin.Context) {
- var data Manager
+ var data struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+ Key string `json:"key,omitempty"`
+ Dots string `json:"dots,omitempty"`
+ X int `json:"x,omitempty"`
+ }
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
+ if h.App.SysConfig.EnabledVerify {
+ var check bool
+ if data.X != 0 {
+ check = h.captcha.SlideCheck(data)
+ } else {
+ check = h.captcha.Check(data)
+ }
+ if !check {
+ resp.ERROR(c, "请先完人机验证")
+ return
+ }
+ }
+
var manager model.AdminUser
res := h.DB.Model(&model.AdminUser{}).Where("username = ?", data.Username).First(&manager)
if res.Error != nil {
diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go
index 07b93700..6d713f53 100644
--- a/api/handler/admin/user_handler.go
+++ b/api/handler/admin/user_handler.go
@@ -49,7 +49,7 @@ func (h *UserHandler) List(c *gin.Context) {
}
session.Model(&model.User{}).Count(&total)
- res := session.Offset(offset).Limit(pageSize).Find(&items)
+ res := session.Offset(offset).Limit(pageSize).Order("id DESC").Find(&items)
if res.Error == nil {
for _, item := range items {
var user vo.User
diff --git a/api/handler/sms_handler.go b/api/handler/sms_handler.go
index 680f73e1..b64962c3 100644
--- a/api/handler/sms_handler.go
+++ b/api/handler/sms_handler.go
@@ -56,15 +56,17 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
resp.ERROR(c, types.InvalidArgs)
return
}
- var check bool
- if data.X != 0 {
- check = h.captcha.SlideCheck(data)
- } else {
- check = h.captcha.Check(data)
- }
- if !check {
- resp.ERROR(c, "验证码错误,请先完人机验证")
- return
+ if h.App.SysConfig.EnabledVerify {
+ var check bool
+ if data.X != 0 {
+ check = h.captcha.SlideCheck(data)
+ } else {
+ check = h.captcha.Check(data)
+ }
+ if !check {
+ resp.ERROR(c, "请先完人机验证")
+ return
+ }
}
code := utils.RandomNumber(6)
diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go
index db39216a..25127eda 100644
--- a/api/handler/user_handler.go
+++ b/api/handler/user_handler.go
@@ -33,6 +33,7 @@ type UserHandler struct {
searcher *xdb.Searcher
redis *redis.Client
licenseService *service.LicenseService
+ captcha *service.CaptchaService
}
func NewUserHandler(
@@ -40,11 +41,13 @@ func NewUserHandler(
db *gorm.DB,
searcher *xdb.Searcher,
client *redis.Client,
+ captcha *service.CaptchaService,
licenseService *service.LicenseService) *UserHandler {
return &UserHandler{
BaseHandler: BaseHandler{DB: db, App: app},
searcher: searcher,
redis: client,
+ captcha: captcha,
licenseService: licenseService,
}
}
@@ -58,6 +61,9 @@ func (h *UserHandler) Register(c *gin.Context) {
Password string `json:"password"`
Code string `json:"code"`
InviteCode string `json:"invite_code"`
+ Key string `json:"key,omitempty"`
+ Dots string `json:"dots,omitempty"`
+ X int `json:"x,omitempty"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
@@ -193,11 +199,28 @@ func (h *UserHandler) Login(c *gin.Context) {
var data struct {
Username string `json:"username"`
Password string `json:"password"`
+ Key string `json:"key,omitempty"`
+ Dots string `json:"dots,omitempty"`
+ X int `json:"x,omitempty"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
+
+ if h.App.SysConfig.EnabledVerify {
+ var check bool
+ if data.X != 0 {
+ check = h.captcha.SlideCheck(data)
+ } else {
+ check = h.captcha.Check(data)
+ }
+ if !check {
+ resp.ERROR(c, "请先完人机验证")
+ return
+ }
+ }
+
var user model.User
res := h.DB.Where("username = ?", data.Username).First(&user)
if res.Error != nil {
diff --git a/web/src/components/LoginDialog.vue b/web/src/components/LoginDialog.vue
index 8fe04551..b6cdd66e 100644
--- a/web/src/components/LoginDialog.vue
+++ b/web/src/components/LoginDialog.vue
@@ -55,7 +55,7 @@
还没有账号?
注册
- 忘记密码?
+ 忘记密码?
@@ -226,6 +226,10 @@
+
+
+
+
@@ -239,12 +243,14 @@ import {Checked, Close, Iphone, Lock, Message} from "@element-plus/icons-vue";
import SendMsg from "@/components/SendMsg.vue";
import {arrayContains} from "@/utils/libs";
import {getSystemInfo} from "@/store/cache";
+import Captcha from "@/components/Captcha.vue";
+import ResetPass from "@/components/ResetPass.vue";
// eslint-disable-next-line no-undef
const props = defineProps({
show: Boolean,
});
-const showDialog = ref(true)
+const showDialog = ref(false)
watch(() => props.show, (newValue) => {
showDialog.value = newValue
})
@@ -260,19 +266,23 @@ const data = ref({
const enableMobile = ref(false)
const enableEmail = ref(false)
const enableUser = ref(false)
-const enableRegister = ref(false)
+const enableRegister = ref(true)
const wechatLoginURL = ref('')
const activeName = ref("")
const wxImg = ref("/images/wx.png")
+const captchaRef = ref(null)
// eslint-disable-next-line no-undef
const emits = defineEmits(['hide', 'success']);
+const action = ref("login")
+const enableVerify = ref(false)
+const showResetPass = ref(false)
onMounted(() => {
const returnURL = `${location.protocol}//${location.host}/login/callback`
httpGet("/api/user/clogin?return_url="+returnURL).then(res => {
wechatLoginURL.value = res.data.url
}).catch(e => {
- console.error(e)
+ console.log(e.message)
})
getSystemInfo().then(res => {
@@ -296,12 +306,21 @@ onMounted(() => {
if (res.data['wechat_card_url'] !== '') {
wxImg.value = res.data['wechat_card_url']
}
+ enableVerify.value = res.data['enabled_verify']
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
})
+const submit = (verifyData) => {
+ if (action.value === "login") {
+ doLogin(verifyData)
+ } else if (action.value === "register") {
+ doRegister(verifyData)
+ }
+}
+
// 登录操作
const submitLogin = () => {
if (data.value.username === '') {
@@ -310,7 +329,18 @@ const submitLogin = () => {
if (data.value.password === '') {
return ElMessage.error('请输入密码');
}
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ action.value = "login"
+ } else {
+ doLogin({})
+ }
+}
+const doLogin = (verifyData) => {
+ data.value.key = verifyData.key
+ data.value.dots = verifyData.dots
+ data.value.x = verifyData.x
httpPost('/api/user/login', data.value).then((res) => {
setUserToken(res.data.token)
ElMessage.success("登录成功!")
@@ -345,9 +375,21 @@ const submitRegister = () => {
if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') {
return ElMessage.error('请输入验证码');
}
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ action.value = "register"
+ } else {
+ doRegister({})
+ }
+}
+
+const doRegister = (verifyData) => {
+ data.value.key = verifyData.key
+ data.value.dots = verifyData.dots
+ data.value.x = verifyData.x
data.value.reg_way = activeName.value
httpPost('/api/user/register', data.value).then((res) => {
- setUserToken(res.data)
+ setUserToken(res.data.token)
ElMessage.success({
"message": "注册成功!",
onClose: () => {
diff --git a/web/src/components/SendMsg.vue b/web/src/components/SendMsg.vue
index 72a30052..f39714a0 100644
--- a/web/src/components/SendMsg.vue
+++ b/web/src/components/SendMsg.vue
@@ -1,10 +1,10 @@
-
+
{{ btnText }}
-
+
@@ -15,6 +15,7 @@ import {validateEmail, validateMobile} from "@/utils/validate";
import {httpPost} from "@/utils/http";
import {showMessageError, showMessageOK} from "@/utils/dialog";
import Captcha from "@/components/Captcha.vue";
+import {getSystemInfo} from "@/store/cache";
// eslint-disable-next-line no-undef
const props = defineProps({
@@ -24,15 +25,24 @@ const props = defineProps({
const btnText = ref('发送验证码')
const canSend = ref(true)
const captchaRef = ref(null)
+const enableVerify = ref(false)
-const showCaptcha = () => {
+getSystemInfo().then(res => {
+ enableVerify.value = res.data['enabled_verify']
+})
+
+const sendMsg = () => {
if (!validateMobile(props.receiver) && !validateEmail(props.receiver)) {
return showMessageError("请输入合法的手机号/邮箱地址")
}
- captchaRef.value.loadCaptcha()
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ } else {
+ doSendMsg({})
+ }
}
-const sendMsg = (data) => {
+const doSendMsg = (data) => {
if (!canSend.value) {
return
}
diff --git a/web/src/store/cache.js b/web/src/store/cache.js
index 04409f60..fdb3284a 100644
--- a/web/src/store/cache.js
+++ b/web/src/store/cache.js
@@ -1,6 +1,5 @@
import {httpGet} from "@/utils/http";
import Storage from "good-storage";
-import {showMessageError} from "@/utils/dialog";
const userDataKey = "USER_INFO_CACHE_KEY"
const adminDataKey = "ADMIN_INFO_CACHE_KEY"
@@ -16,12 +15,12 @@ export function checkSession() {
httpGet('/api/user/session').then(res => {
item.data = res.data
// cache expires after 10 secs
- item.expire = Date.now() + 1000 * 10
+ item.expire = Date.now() + 1000 * 60 * 5
Storage.set(userDataKey, item)
resolve(item.data)
- }).catch(err => {
+ }).catch(e => {
Storage.remove(userDataKey)
- reject(err)
+ reject(e)
})
})
}
@@ -38,11 +37,12 @@ export function checkAdminSession() {
return new Promise((resolve, reject) => {
httpGet('/api/admin/session').then(res => {
item.data = res.data
- item.expire = Date.now() + 1000 * 10
+ item.expire = Date.now() + 1000 * 60 * 5
Storage.set(adminDataKey, item)
resolve(item.data)
- }).catch(err => {
- reject(err)
+ }).catch(e => {
+ Storage.remove(adminDataKey)
+ reject(e)
})
})
}
@@ -59,11 +59,11 @@ export function getSystemInfo() {
return new Promise((resolve, reject) => {
httpGet('/api/config/get?key=system').then(res => {
item.data = res
- item.expire = Date.now() + 1000 * 10
+ item.expire = Date.now() + 1000 * 60 * 10
Storage.set(systemInfoKey, item)
resolve(item.data)
}).catch(err => {
- reject(err)
+ resolve(err)
})
})
}
@@ -77,11 +77,11 @@ export function getLicenseInfo() {
return new Promise((resolve, reject) => {
httpGet('/api/config/license').then(res => {
item.data = res
- item.expire = Date.now() + 1000 * 10
+ item.expire = Date.now() + 1000 * 60 * 10
Storage.set(licenseInfoKey, item)
resolve(item.data)
}).catch(err => {
- reject(err)
+ resolve(err)
})
})
}
\ No newline at end of file
diff --git a/web/src/store/session.js b/web/src/store/session.js
index 2f97602f..3f023555 100644
--- a/web/src/store/session.js
+++ b/web/src/store/session.js
@@ -1,6 +1,6 @@
import {randString} from "@/utils/libs";
import Storage from "good-storage";
-import {removeAdminInfo, removeUserInfo} from "@/store/cache";
+import {checkAdminSession, checkSession, removeAdminInfo, removeUserInfo} from "@/store/cache";
/**
* storage handler
@@ -18,6 +18,7 @@ export function getUserToken() {
}
export function setUserToken(token) {
+ // 刷新 session 缓存
Storage.set(UserTokenKey, token)
}
diff --git a/web/src/views/ChatApps.vue b/web/src/views/ChatApps.vue
index d0e589dc..48193f8e 100644
--- a/web/src/views/ChatApps.vue
+++ b/web/src/views/ChatApps.vue
@@ -89,7 +89,8 @@ onMounted(() => {
const getRoles = () => {
checkSession().then(user => {
roles.value = user.chat_roles
- }).catch(() => {
+ }).catch(e => {
+ console.log(e.message)
})
}
diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue
index 298721cd..cdd49a69 100644
--- a/web/src/views/Home.vue
+++ b/web/src/views/Home.vue
@@ -72,7 +72,6 @@
登录
- 注册
@@ -161,7 +160,7 @@ const docsURL = ref(process.env.VUE_APP_DOCS_URL)
const gitURL = ref(process.env.VUE_APP_GIT_URL)
const store = useSharedStore();
-const show = ref(true)
+const show = ref(false)
watch(() => store.showLoginDialog, (newValue) => {
show.value = newValue
});
diff --git a/web/src/views/Login.vue b/web/src/views/Login.vue
index 09840ef2..05b443e9 100644
--- a/web/src/views/Login.vue
+++ b/web/src/views/Login.vue
@@ -55,6 +55,8 @@
+
+
@@ -73,6 +75,7 @@ import {checkSession, getLicenseInfo, getSystemInfo} from "@/store/cache";
import {setUserToken} from "@/store/session";
import ResetPass from "@/components/ResetPass.vue";
import {showMessageError} from "@/utils/dialog";
+import Captcha from "@/components/Captcha.vue";
const router = useRouter();
const title = ref('Geek-AI');
@@ -82,12 +85,15 @@ const showResetPass = ref(false)
const logo = ref("")
const licenseConfig = ref({})
const wechatLoginURL = ref('')
+const enableVerify = ref(false)
+const captchaRef = ref(null)
onMounted(() => {
// 获取系统配置
getSystemInfo().then(res => {
logo.value = res.data.logo
title.value = res.data.title
+ enableVerify.value = res.data['enabled_verify']
}).catch(e => {
showMessageError("获取系统配置失败:" + e.message)
})
@@ -129,7 +135,21 @@ const login = function () {
return showMessageError('请输入密码');
}
- httpPost('/api/user/login', {username: username.value.trim(), password: password.value.trim()}).then((res) => {
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ } else {
+ doLogin({})
+ }
+}
+
+const doLogin = (verifyData) => {
+ httpPost('/api/user/login', {
+ username: username.value.trim(),
+ password: password.value.trim(),
+ key: verifyData.key,
+ dots: verifyData.dots,
+ x: verifyData.x
+ }).then((res) => {
setUserToken(res.data.token)
if (isMobile()) {
router.push('/mobile')
diff --git a/web/src/views/Register.vue b/web/src/views/Register.vue
index f080b568..979b7a7f 100644
--- a/web/src/views/Register.vue
+++ b/web/src/views/Register.vue
@@ -160,6 +160,8 @@
+
+
@@ -182,6 +184,7 @@ import {setUserToken} from "@/store/session";
import {validateEmail, validateMobile} from "@/utils/validate";
import {showMessageError, showMessageOK} from "@/utils/dialog";
import {getLicenseInfo, getSystemInfo} from "@/store/cache";
+import Captcha from "@/components/Captcha.vue";
const router = useRouter();
const title = ref('');
@@ -201,6 +204,8 @@ const enableRegister = ref(true)
const activeName = ref("mobile")
const wxImg = ref("/images/wx.png")
const licenseConfig = ref({})
+const enableVerify = ref(false)
+const captchaRef = ref(null)
getSystemInfo().then(res => {
if (res.data) {
@@ -222,6 +227,7 @@ getSystemInfo().then(res => {
if (res.data['wechat_card_url'] !== '') {
wxImg.value = res.data['wechat_card_url']
}
+ enableVerify.value = res.data['enabled_verify']
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
@@ -257,9 +263,21 @@ const submitRegister = () => {
if ((activeName.value === 'mobile' || activeName.value === 'email') && data.value.code === '') {
return showMessageError('请输入验证码');
}
+
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ } else {
+ doSubmitRegister({})
+ }
+}
+
+const doSubmitRegister = (verifyData) => {
+ data.value.key = verifyData.key
+ data.value.dots = verifyData.dots
+ data.value.x = verifyData.x
data.value.reg_way = activeName.value
httpPost('/api/user/register', data.value).then((res) => {
- setUserToken(res.data)
+ setUserToken(res.data.token)
showMessageOK({
"message": "注册成功,即将跳转到对话主界面...",
onClose: () => router.push("/chat"),
diff --git a/web/src/views/admin/Login.vue b/web/src/views/admin/Login.vue
index 37d17458..4c326c93 100644
--- a/web/src/views/admin/Login.vue
+++ b/web/src/views/admin/Login.vue
@@ -37,6 +37,8 @@
+
+
@@ -54,12 +56,15 @@ import {useRouter} from "vue-router";
import FooterBar from "@/components/FooterBar.vue";
import {setAdminToken} from "@/store/session";
import {checkAdminSession, getSystemInfo} from "@/store/cache";
+import Captcha from "@/components/Captcha.vue";
const router = useRouter();
const title = ref('Geek-AI Console');
const username = ref(process.env.VUE_APP_ADMIN_USER);
const password = ref(process.env.VUE_APP_ADMIN_PASS);
const logo = ref("")
+const enableVerify = ref(false)
+const captchaRef = ref(null)
checkAdminSession().then(() => {
router.push("/admin")
@@ -70,6 +75,7 @@ checkAdminSession().then(() => {
getSystemInfo().then(res => {
title.value = res.data.admin_title
logo.value = res.data.logo
+ enableVerify.value = res.data['enabled_verify']
}).catch(e => {
ElMessage.error("加载系统配置失败: " + e.message)
})
@@ -87,8 +93,21 @@ const login = function () {
if (password.value === '') {
return ElMessage.error('请输入密码');
}
+ if (enableVerify.value) {
+ captchaRef.value.loadCaptcha()
+ } else {
+ doLogin({})
+ }
+}
- httpPost('/api/admin/login', {username: username.value.trim(), password: password.value.trim()}).then(res => {
+const doLogin = function (verifyData) {
+ httpPost('/api/admin/login', {
+ username: username.value.trim(),
+ password: password.value.trim(),
+ key: verifyData.key,
+ dots: verifyData.dots,
+ x: verifyData.x
+ }).then(res => {
setAdminToken(res.data.token)
router.push("/admin")
}).catch((e) => {