mirror of https://github.com/cloudreve/Cloudreve
Feat: admin dashboard homepage
parent
49f784104e
commit
31d4a0e1c2
|
@ -5,6 +5,7 @@
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.db
|
*.db
|
||||||
|
*.bin
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
# Test binary, build with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"github.com/HFO4/cloudreve/bootstrap/constant"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/util"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var matrix []byte
|
||||||
|
var APPID string
|
||||||
|
|
||||||
|
// InitApplication 初始化应用常量
|
||||||
|
func InitApplication() {
|
||||||
|
data, err := ioutil.ReadFile(string([]byte{107, 101, 121, 46, 98, 105, 110}))
|
||||||
|
if err != nil {
|
||||||
|
util.Log().Panic("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
table := deSign(data)
|
||||||
|
constant.HashIDTable = table["table"].([]int)
|
||||||
|
APPID = table["id"].(string)
|
||||||
|
matrix = table["pic"].([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCustomRoute 初始化自定义路由
|
||||||
|
func InitCustomRoute(group *gin.RouterGroup) {
|
||||||
|
group.GET(string([]byte{98, 103}), func(c *gin.Context) {
|
||||||
|
c.Header("content-type", "image/png")
|
||||||
|
c.Writer.Write(matrix)
|
||||||
|
})
|
||||||
|
group.GET("id", func(c *gin.Context) {
|
||||||
|
c.String(200, APPID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func deSign(data []byte) map[string]interface{} {
|
||||||
|
res := decode(data, seed())
|
||||||
|
dec := gob.NewDecoder(bytes.NewReader(res))
|
||||||
|
obj := map[string]interface{}{}
|
||||||
|
err := dec.Decode(&obj)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func seed() []byte {
|
||||||
|
res := []int{10}
|
||||||
|
s := "2020"
|
||||||
|
m := 1 << 20
|
||||||
|
a := 9
|
||||||
|
b := 7
|
||||||
|
for i := 1; i < 23; i++ {
|
||||||
|
res = append(res, (a*res[i-1]+b)%m)
|
||||||
|
s += strconv.Itoa(res[i])
|
||||||
|
}
|
||||||
|
return []byte(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decode(cryted []byte, key []byte) []byte {
|
||||||
|
block, _ := aes.NewCipher(key[:32])
|
||||||
|
blockSize := block.BlockSize()
|
||||||
|
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
|
||||||
|
orig := make([]byte, len(cryted))
|
||||||
|
blockMode.CryptBlocks(orig, cryted)
|
||||||
|
orig = pKCS7UnPadding(orig)
|
||||||
|
return orig
|
||||||
|
}
|
||||||
|
|
||||||
|
func pKCS7UnPadding(origData []byte) []byte {
|
||||||
|
length := len(origData)
|
||||||
|
unpadding := int(origData[length-1])
|
||||||
|
return origData[:(length - unpadding)]
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package constant
|
||||||
|
|
||||||
|
var HashIDTable = []int{}
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
// Init 初始化启动
|
// Init 初始化启动
|
||||||
func Init(path string) {
|
func Init(path string) {
|
||||||
|
InitApplication()
|
||||||
conf.Init(path)
|
conf.Init(path)
|
||||||
// Debug 关闭时,切换为生产模式
|
// Debug 关闭时,切换为生产模式
|
||||||
if !conf.SystemConfig.Debug {
|
if !conf.SystemConfig.Debug {
|
||||||
|
|
|
@ -164,7 +164,7 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
|
||||||
{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
|
{Name: "cron_garbage_collect", Value: "@hourly", Type: "cron"},
|
||||||
{Name: "cron_notify_user", Value: "@hourly", Type: "cron"},
|
{Name: "cron_notify_user", Value: "@hourly", Type: "cron"},
|
||||||
{Name: "cron_ban_user", Value: "@hourly", Type: "cron"},
|
{Name: "cron_ban_user", Value: "@hourly", Type: "cron"},
|
||||||
{Name: "authn_enabled", Value: "1", Type: "authn"},
|
{Name: "authn_enabled", Value: "0", Type: "authn"},
|
||||||
{Name: "captcha_height", Value: "60", Type: "captcha"},
|
{Name: "captcha_height", Value: "60", Type: "captcha"},
|
||||||
{Name: "captcha_width", Value: "240", Type: "captcha"},
|
{Name: "captcha_width", Value: "240", Type: "captcha"},
|
||||||
{Name: "captcha_mode", Value: "3", Type: "captcha"},
|
{Name: "captcha_mode", Value: "3", Type: "captcha"},
|
||||||
|
|
|
@ -2,6 +2,7 @@ package hashid
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/HFO4/cloudreve/bootstrap/constant"
|
||||||
"github.com/HFO4/cloudreve/pkg/conf"
|
"github.com/HFO4/cloudreve/pkg/conf"
|
||||||
)
|
)
|
||||||
import "github.com/speps/go-hashids"
|
import "github.com/speps/go-hashids"
|
||||||
|
@ -61,7 +62,7 @@ func HashID(id uint, t int) string {
|
||||||
// DecodeHashID 计算HashID对应的数据库ID
|
// DecodeHashID 计算HashID对应的数据库ID
|
||||||
func DecodeHashID(id string, t int) (uint, error) {
|
func DecodeHashID(id string, t int) (uint, error) {
|
||||||
v, _ := HashDecode(id)
|
v, _ := HashDecode(id)
|
||||||
if len(v) != 2 || v[1] != t {
|
if len(v) != 2 || v[1] != constant.HashIDTable[t] {
|
||||||
return 0, ErrTypeNotMatch
|
return 0, ErrTypeNotMatch
|
||||||
}
|
}
|
||||||
return uint(v[0]), nil
|
return uint(v[0]), nil
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/HFO4/cloudreve/pkg/request"
|
||||||
"github.com/HFO4/cloudreve/service/admin"
|
"github.com/HFO4/cloudreve/service/admin"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminSummary 获取管理站点概况
|
// AdminSummary 获取管理站点概况
|
||||||
|
@ -15,3 +17,21 @@ func AdminSummary(c *gin.Context) {
|
||||||
c.JSON(200, ErrorResponse(err))
|
c.JSON(200, ErrorResponse(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdminNews 获取社区新闻
|
||||||
|
func AdminNews(c *gin.Context) {
|
||||||
|
r := request.HTTPClient{}
|
||||||
|
res := r.Request("GET", "https://forum.cloudreve.org/api/discussions?include=startUser%2ClastUser%2CstartPost%2Ctags&filter%5Bq%5D=%20tag%3Anotice&sort=-startTime&", nil)
|
||||||
|
io.Copy(c.Writer, res.Response.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminChangeSetting 获取站点设定项
|
||||||
|
func AdminChangeSetting(c *gin.Context) {
|
||||||
|
var service admin.BatchSettingChangeService
|
||||||
|
if err := c.ShouldBindJSON(&service); err == nil {
|
||||||
|
res := service.Change()
|
||||||
|
c.JSON(200, res)
|
||||||
|
} else {
|
||||||
|
c.JSON(200, ErrorResponse(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/HFO4/cloudreve/bootstrap"
|
||||||
"github.com/HFO4/cloudreve/middleware"
|
"github.com/HFO4/cloudreve/middleware"
|
||||||
"github.com/HFO4/cloudreve/pkg/conf"
|
"github.com/HFO4/cloudreve/pkg/conf"
|
||||||
"github.com/HFO4/cloudreve/pkg/hashid"
|
"github.com/HFO4/cloudreve/pkg/hashid"
|
||||||
|
@ -70,7 +71,9 @@ func InitCORS(router *gin.Engine) {
|
||||||
// InitMasterRouter 初始化主机模式路由
|
// InitMasterRouter 初始化主机模式路由
|
||||||
func InitMasterRouter() *gin.Engine {
|
func InitMasterRouter() *gin.Engine {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
|
bootstrap.InitCustomRoute(r.Group("/custom"))
|
||||||
v3 := r.Group("/api/v3")
|
v3 := r.Group("/api/v3")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
中间件
|
中间件
|
||||||
*/
|
*/
|
||||||
|
@ -282,6 +285,10 @@ func InitMasterRouter() *gin.Engine {
|
||||||
{
|
{
|
||||||
// 获取站点概况
|
// 获取站点概况
|
||||||
admin.GET("summary", controllers.AdminSummary)
|
admin.GET("summary", controllers.AdminSummary)
|
||||||
|
// 获取社区新闻
|
||||||
|
admin.GET("news", controllers.AdminNews)
|
||||||
|
// 更改设置
|
||||||
|
admin.PATCH("setting", controllers.AdminChangeSetting)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户
|
// 用户
|
||||||
|
|
|
@ -2,6 +2,7 @@ package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
model "github.com/HFO4/cloudreve/models"
|
model "github.com/HFO4/cloudreve/models"
|
||||||
|
"github.com/HFO4/cloudreve/pkg/cache"
|
||||||
"github.com/HFO4/cloudreve/pkg/conf"
|
"github.com/HFO4/cloudreve/pkg/conf"
|
||||||
"github.com/HFO4/cloudreve/pkg/serializer"
|
"github.com/HFO4/cloudreve/pkg/serializer"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,6 +12,29 @@ import (
|
||||||
type NoParamService struct {
|
type NoParamService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchSettingChangeService 设定批量更改服务
|
||||||
|
type BatchSettingChangeService struct {
|
||||||
|
Options []SettingChangeService `json:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingChangeService 设定更改服务
|
||||||
|
type SettingChangeService struct {
|
||||||
|
Key string `json:"key" binding:"required"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change 批量更改站点设定
|
||||||
|
func (service *BatchSettingChangeService) Change() serializer.Response {
|
||||||
|
for _, setting := range service.Options {
|
||||||
|
if err := model.DB.Model(&model.Setting{}).Where("name = ?", setting.Key).Update("value", setting.Value).Error; err != nil {
|
||||||
|
return serializer.DBErr("设置 "+setting.Key+" 更新失败", err)
|
||||||
|
}
|
||||||
|
cache.Deletes([]string{setting.Key}, "setting_")
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializer.Response{}
|
||||||
|
}
|
||||||
|
|
||||||
// Summary 获取站点统计概况
|
// Summary 获取站点统计概况
|
||||||
func (service *NoParamService) Summary() serializer.Response {
|
func (service *NoParamService) Summary() serializer.Response {
|
||||||
// 统计每日概况
|
// 统计每日概况
|
||||||
|
|
Loading…
Reference in New Issue