feat: add system config for enable rand background image for index page

This commit is contained in:
RockYang 2024-05-29 16:23:42 +08:00
parent 89f6402fbf
commit dd07acd21a
13 changed files with 137 additions and 86 deletions

View File

@ -227,7 +227,6 @@ func needLogin(c *gin.Context) bool {
c.Request.URL.Path == "/api/sd/client" || c.Request.URL.Path == "/api/sd/client" ||
c.Request.URL.Path == "/api/dall/imgWall" || c.Request.URL.Path == "/api/dall/imgWall" ||
c.Request.URL.Path == "/api/dall/client" || c.Request.URL.Path == "/api/dall/client" ||
c.Request.URL.Path == "/api/config/get" ||
c.Request.URL.Path == "/api/product/list" || c.Request.URL.Path == "/api/product/list" ||
c.Request.URL.Path == "/api/menu/list" || c.Request.URL.Path == "/api/menu/list" ||
c.Request.URL.Path == "/api/markMap/client" || c.Request.URL.Path == "/api/markMap/client" ||
@ -237,6 +236,7 @@ func needLogin(c *gin.Context) bool {
c.Request.URL.Path == "/api/payment/doPay" || c.Request.URL.Path == "/api/payment/doPay" ||
c.Request.URL.Path == "/api/payment/payWays" || c.Request.URL.Path == "/api/payment/payWays" ||
strings.HasPrefix(c.Request.URL.Path, "/api/test") || strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
strings.HasPrefix(c.Request.URL.Path, "/api/config/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/function/") || strings.HasPrefix(c.Request.URL.Path, "/api/function/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") || strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") || strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||

View File

@ -128,9 +128,14 @@ const LicenseKey = "Geek-AI-License"
type License struct { type License struct {
Key string `json:"key"` // 许可证书密钥 Key string `json:"key"` // 许可证书密钥
MachineId string `json:"machine_id"` // 机器码 MachineId string `json:"machine_id"` // 机器码
UserNum int `json:"user_num"` // 用户数量
ExpiredAt int64 `json:"expired_at"` // 过期时间 ExpiredAt int64 `json:"expired_at"` // 过期时间
IsActive bool `json:"is_active"` // 是否激活 IsActive bool `json:"is_active"` // 是否激活
Configs LicenseConfig `json:"configs"`
}
type LicenseConfig struct {
UserNum int `json:"user_num"` // 用户数量
DeCopy bool `json:"de_copy"` // 去版权
} }
func (c RedisConfig) Url() string { func (c RedisConfig) Url() string {
@ -207,4 +212,6 @@ type SystemConfig struct {
ContextDeep int `json:"context_deep,omitempty"` ContextDeep int `json:"context_deep,omitempty"`
SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词 SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词
RandBg bool `json:"rand_bg"` // 前端首页是否启用随机背景
} }

View File

@ -87,7 +87,7 @@ func (h *UserHandler) Save(c *gin.Context) {
// 检测最大注册人数 // 检测最大注册人数
var totalUser int64 var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser) h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().UserNum { if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License") resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return return
} }

View File

@ -9,6 +9,7 @@ package handler
import ( import (
"geekai/core" "geekai/core"
"geekai/service"
"geekai/store/model" "geekai/store/model"
"geekai/utils" "geekai/utils"
"geekai/utils/resp" "geekai/utils/resp"
@ -19,10 +20,11 @@ import (
type ConfigHandler struct { type ConfigHandler struct {
BaseHandler BaseHandler
licenseService *service.LicenseService
} }
func NewConfigHandler(app *core.AppServer, db *gorm.DB) *ConfigHandler { func NewConfigHandler(app *core.AppServer, db *gorm.DB, licenseService *service.LicenseService) *ConfigHandler {
return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}} return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}, licenseService: licenseService}
} }
// Get 获取指定的系统配置 // Get 获取指定的系统配置
@ -44,3 +46,9 @@ func (h *ConfigHandler) Get(c *gin.Context) {
resp.SUCCESS(c, value) resp.SUCCESS(c, value)
} }
// License 获取 License 配置
func (h *ConfigHandler) License(c *gin.Context) {
license := h.licenseService.GetLicense()
resp.SUCCESS(c, license.Configs)
}

View File

@ -71,7 +71,7 @@ func (h *UserHandler) Register(c *gin.Context) {
// 检测最大注册人数 // 检测最大注册人数
var totalUser int64 var totalUser int64
h.DB.Model(&model.User{}).Count(&totalUser) h.DB.Model(&model.User{}).Count(&totalUser)
if h.licenseService.GetLicense().UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().UserNum { if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum {
resp.ERROR(c, "当前注册用户数已达上限,请请升级 License") resp.ERROR(c, "当前注册用户数已达上限,请请升级 License")
return return
} }

View File

@ -295,6 +295,7 @@ func main() {
fx.Invoke(func(s *core.AppServer, h *handler.ConfigHandler) { fx.Invoke(func(s *core.AppServer, h *handler.ConfigHandler) {
group := s.Engine.Group("/api/config/") group := s.Engine.Group("/api/config/")
group.GET("get", h.Get) group.GET("get", h.Get)
group.GET("license", h.License)
}), }),
// 管理后台控制器 // 管理后台控制器

View File

@ -52,6 +52,7 @@ type License struct {
ActiveAt int64 `json:"active_at"` ActiveAt int64 `json:"active_at"`
ExpiredAt int64 `json:"expired_at"` ExpiredAt int64 `json:"expired_at"`
UserNum int `json:"user_num"` UserNum int `json:"user_num"`
Configs types.LicenseConfig `json:"configs"`
} }
// ActiveLicense 激活 License // ActiveLicense 激活 License
@ -80,7 +81,7 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error {
s.license = &types.License{ s.license = &types.License{
Key: license, Key: license,
MachineId: machineId, MachineId: machineId,
UserNum: res.Data.UserNum, Configs: res.Data.Configs,
ExpiredAt: res.Data.ExpiredAt, ExpiredAt: res.Data.ExpiredAt,
IsActive: true, IsActive: true,
} }
@ -140,7 +141,7 @@ func (s *LicenseService) fetchLicense() (*types.License, error) {
return &types.License{ return &types.License{
Key: res.Data.License, Key: res.Data.License,
MachineId: res.Data.MachineId, MachineId: res.Data.MachineId,
UserNum: res.Data.UserNum, Configs: res.Data.Configs,
ExpiredAt: res.Data.ExpiredAt, ExpiredAt: res.Data.ExpiredAt,
IsActive: true, IsActive: true,
}, nil }, nil

View File

@ -11,8 +11,8 @@
</div> </div>
<div class="navbar"> <div class="navbar">
<el-tooltip <el-tooltip
v-if="!licenseConfig.de_copy"
class="box-item" class="box-item"
effect="light" effect="light"
content="部署文档" content="部署文档"
@ -23,6 +23,7 @@
</el-tooltip> </el-tooltip>
<el-tooltip <el-tooltip
v-if="!licenseConfig.de_copy"
class="box-item" class="box-item"
effect="light" effect="light"
content="项目源码" content="项目源码"
@ -140,6 +141,7 @@ import {removeUserToken} from "@/store/session";
import LoginDialog from "@/components/LoginDialog.vue"; import LoginDialog from "@/components/LoginDialog.vue";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import ConfigDialog from "@/components/ConfigDialog.vue"; import ConfigDialog from "@/components/ConfigDialog.vue";
import {showMessageError} from "@/utils/dialog";
const router = useRouter(); const router = useRouter();
const logo = ref('/images/logo.png'); const logo = ref('/images/logo.png');
@ -152,6 +154,7 @@ const loginUser = ref({})
const version = ref(process.env.VUE_APP_VERSION) const version = ref(process.env.VUE_APP_VERSION)
const routerViewKey = ref(0) const routerViewKey = ref(0)
const showConfigDialog = ref(false) const showConfigDialog = ref(false)
const licenseConfig = ref({})
const store = useSharedStore(); const store = useSharedStore();
const show = ref(false) const show = ref(false)
@ -191,6 +194,12 @@ onMounted(() => {
ElMessage.error("获取系统菜单失败:" + e.message) ElMessage.error("获取系统菜单失败:" + e.message)
}) })
httpGet("/api/config/license").then(res => {
licenseConfig.value = res.data
}).catch(e => {
showMessageError("获取 License 配置:" + e.message)
})
init() init()
}) })

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="index-page" :style="{height: winHeight+'px'}"> <div class="index-page" :style="{height: winHeight+'px'}">
<div class="bg"></div> <div :class="bgClass"></div>
<div class="menu-box"> <div class="menu-box">
<el-menu <el-menu
mode="horizontal" mode="horizontal"
@ -11,6 +11,7 @@
<div class="title">{{ title }}</div> <div class="title">{{ title }}</div>
</div> </div>
<div class="menu-item"> <div class="menu-item">
<span v-if="!licenseConfig.de_copy">
<a href="https://ai.r9it.com/docs/install/" target="_blank"> <a href="https://ai.r9it.com/docs/install/" target="_blank">
<el-button type="primary" round> <el-button type="primary" round>
<i class="iconfont icon-book"></i> <i class="iconfont icon-book"></i>
@ -24,6 +25,7 @@
<span>项目源码</span> <span>项目源码</span>
</el-button> </el-button>
</a> </a>
</span>
<el-button @click="router.push('/login')" round>登录</el-button> <el-button @click="router.push('/login')" round>登录</el-button>
<el-button @click="router.push('/register')" round>注册</el-button> <el-button @click="router.push('/register')" round>注册</el-button>
</div> </div>
@ -52,7 +54,7 @@
<!-- <div id="animation-container"></div>--> <!-- <div id="animation-container"></div>-->
</div> </div>
<div class="footer"> <div class="footer" v-if="!licenseConfig.de_copy">
<footer-bar /> <footer-bar />
</div> </div>
</div> </div>
@ -77,73 +79,31 @@ if (isMobile()) {
const title = ref("Geek-AI 创作系统") const title = ref("Geek-AI 创作系统")
const logo = ref("/images/logo.png") const logo = ref("/images/logo.png")
const slogan = ref("我辈之人,先干为敬,陪您先把 AI 用起来") const slogan = ref("我辈之人,先干为敬,陪您先把 AI 用起来")
const licenseConfig = ref({})
// const size = Math.max(window.innerWidth * 0.5, window.innerHeight * 0.8) // const size = Math.max(window.innerWidth * 0.5, window.innerHeight * 0.8)
const winHeight = window.innerHeight - 150 const winHeight = window.innerHeight - 150
const bgClass = ref('fixed-bg')
onMounted(() => { onMounted(() => {
httpGet("/api/config/get?key=system").then(res => { httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title title.value = res.data.title
logo.value = res.data.logo logo.value = res.data.logo
if (res.data.rand_bg) {
bgClass.value = "rand-bg"
}
}).catch(e => { }).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message) ElMessage.error("获取系统配置失败:" + e.message)
}) })
httpGet("/api/config/license").then(res => {
licenseConfig.value = res.data
}).catch(e => {
ElMessage.error("获取 License 配置:" + e.message)
})
init() init()
}) })
const init = () => { const init = () => {
// //
// //
// const scene = new THREE.Scene();
//
// //
// const camera = new THREE.PerspectiveCamera(30, 1, 0.1, 1000);
// camera.position.z = 3.88;
//
// //
// const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
// renderer.setSize(size, size);
// renderer.setClearColor(0x000000, 0);
// const container = document.getElementById('animation-container');
// container.appendChild(renderer.domElement);
//
// //
// const loader = new THREE.TextureLoader();
// loader.load(
// '/images/land_ocean_ice_cloud_2048.jpg',
// function (texture) {
// //
// const geometry = new THREE.SphereGeometry(1, 32, 32);
// const material = new THREE.MeshPhongMaterial({
// map: texture,
// bumpMap: texture, // 使
// bumpScale: 0.05, //
// specularMap: texture, //
// specular: new THREE.Color('#01193B'), //
// });
// const earth = new THREE.Mesh(geometry, material);
// scene.add(earth);
//
// //
// const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
// scene.add(ambientLight);
// const pointLight = new THREE.PointLight(0xffffff, 0.8);
// pointLight.position.set(5, 5, 5);
// scene.add(pointLight);
//
// //
// const animate = function () {
// requestAnimationFrame(animate);
//
// // 使
// earth.rotation.y += 0.0006;
//
// renderer.render(scene, camera);
// };
//
// //
// animate();
// }
// );
} }
</script> </script>
@ -158,14 +118,25 @@ const init = () => {
align-items baseline align-items baseline
padding-top 150px padding-top 150px
.bg { .fixed-bg {
position absolute position absolute
top 0 top 0
left 0 left 0
width 100vw width 100vw
height 100vh height 100vh
background-image url("~@/assets/img/ai-bg.jpg") background-image url("~@/assets/img/ai-bg.jpg")
//filter: blur(8px); background-size: cover;
background-position: center;
}
.rand-bg {
position absolute
top 0
left 0
width 100vw
height 100vh
background-image url("https://api.dujin.org/bing/1920.php")
filter: blur(8px);
background-size: cover; background-size: cover;
background-position: center; background-position: center;
} }

View File

@ -48,7 +48,7 @@
<reset-pass @hide="showResetPass = false" :show="showResetPass"/> <reset-pass @hide="showResetPass = false" :show="showResetPass"/>
<footer class="footer"> <footer class="footer" v-if="!licenseConfig.de_copy">
<footer-bar/> <footer-bar/>
</footer> </footer>
</div> </div>
@ -74,6 +74,7 @@ const username = ref(process.env.VUE_APP_USER);
const password = ref(process.env.VUE_APP_PASS); const password = ref(process.env.VUE_APP_PASS);
const showResetPass = ref(false) const showResetPass = ref(false)
const logo = ref("/images/logo.png") const logo = ref("/images/logo.png")
const licenseConfig = ref({})
// //
httpGet("/api/config/get?key=system").then(res => { httpGet("/api/config/get?key=system").then(res => {
@ -83,6 +84,11 @@ httpGet("/api/config/get?key=system").then(res => {
showMessageError("获取系统配置失败:" + e.message) showMessageError("获取系统配置失败:" + e.message)
}) })
httpGet("/api/config/license").then(res => {
licenseConfig.value = res.data
}).catch(e => {
showMessageError("获取 License 配置:" + e.message)
})
checkSession().then(() => { checkSession().then(() => {
if (isMobile()) { if (isMobile()) {

View File

@ -160,7 +160,7 @@
</el-result> </el-result>
</div> </div>
<footer class="footer"> <footer class="footer" v-if="!licenseConfig.de_copy">
<footer-bar/> <footer-bar/>
</footer> </footer>
</div> </div>
@ -199,6 +199,7 @@ const enableUser = ref(false)
const enableRegister = ref(false) const enableRegister = ref(false)
const activeName = ref("mobile") const activeName = ref("mobile")
const wxImg = ref("/images/wx.png") const wxImg = ref("/images/wx.png")
const licenseConfig = ref({})
httpGet("/api/config/get?key=system").then(res => { httpGet("/api/config/get?key=system").then(res => {
if (res.data) { if (res.data) {
@ -225,6 +226,12 @@ httpGet("/api/config/get?key=system").then(res => {
ElMessage.error("获取系统配置失败:" + e.message) ElMessage.error("获取系统配置失败:" + e.message)
}) })
httpGet("/api/config/license").then(res => {
licenseConfig.value = res.data
}).catch(e => {
showMessageError("获取 License 配置:" + e.message)
})
// //
const submitRegister = () => { const submitRegister = () => {
if (data.value.username === '') { if (data.value.username === '') {

View File

@ -133,6 +133,8 @@
<template #default="scope"> <template #default="scope">
<div class="context-msg-content"> <div class="context-msg-content">
<el-input <el-input
type="textarea"
:rows="2"
v-model="scope.row.content" v-model="scope.row.content"
autocomplete="off" autocomplete="off"
/> />

View File

@ -44,6 +44,20 @@
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item label="随机背景">
<el-switch v-model="system['rand_bg']"/>
<el-tooltip
effect="dark"
content="打开之后前端首页将使用随机壁纸作为背景图"
raw-content
placement="right"
>
<el-icon>
<InfoFilled/>
</el-icon>
</el-tooltip>
</el-form-item>
<el-form-item label="注册方式" prop="register_ways"> <el-form-item label="注册方式" prop="register_ways">
<el-checkbox-group v-model="system['register_ways']"> <el-checkbox-group v-model="system['register_ways']">
<el-checkbox value="mobile">手机注册</el-checkbox> <el-checkbox value="mobile">手机注册</el-checkbox>
@ -298,16 +312,22 @@
<el-descriptions <el-descriptions
v-if="license.is_active" v-if="license.is_active"
class="margin-top" class="margin-top"
title="授权信息" title="授权信息"
:column="3" :column="1"
border border
> >
<el-descriptions-item :span="3" :width="150"> <el-descriptions-item>
<template #label> <template #label>
<div class="cell-item">License Key</div> <div class="cell-item">License Key</div>
</template> </template>
{{ license.key }} {{ license.key }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">机器码</div>
</template>
{{ license.machine_id }}
</el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<div class="cell-item">到期时间</div> <div class="cell-item">到期时间</div>
@ -318,13 +338,15 @@
<template #label> <template #label>
<div class="cell-item">用户人数</div> <div class="cell-item">用户人数</div>
</template> </template>
{{ license.user_num }} {{ license.configs?.user_num }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<div class="cell-item">机器码</div> <div class="cell-item">去版权</div>
</template> </template>
{{ license.machine_id }} <el-icon class="selected" v-if="license.configs?.de_copy"><Select /></el-icon>
<el-icon class="closed" v-else><CloseBold /></el-icon>
<span class="text">去版权之后前端页面将不会显示版权信息和源码地址</span>
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@ -348,7 +370,7 @@ import {onMounted, reactive, ref} from "vue";
import {httpGet, httpPost} from "@/utils/http"; import {httpGet, httpPost} from "@/utils/http";
import Compressor from "compressorjs"; import Compressor from "compressorjs";
import {ElMessage} from "element-plus"; import {ElMessage} from "element-plus";
import {InfoFilled, UploadFilled} from "@element-plus/icons-vue"; import {InfoFilled, UploadFilled,Select,CloseBold} from "@element-plus/icons-vue";
import MdEditor from "md-editor-v3"; import MdEditor from "md-editor-v3";
import 'md-editor-v3/lib/style.css'; import 'md-editor-v3/lib/style.css';
import Menu from "@/views/admin/Menu.vue"; import Menu from "@/views/admin/Menu.vue";
@ -536,6 +558,23 @@ const onUploadImg = (files, callback) => {
.el-descriptions { .el-descriptions {
margin-bottom 20px margin-bottom 20px
.el-icon {
font-size 18px
}
.selected {
color #0bc15f
}
.closed {
color #da0d54
}
.text {
margin-left 10px
font-size 12px
color #999999
position: relative;
top -5px
}
} }
.el-alert { .el-alert {