数据源和 oss
This commit is contained in:
parent
f1a0b17bb0
commit
c4bf7ee03b
|
|
@ -31,7 +31,7 @@ Thumbs.db
|
|||
*.key
|
||||
*.log
|
||||
bin/
|
||||
/cmd/kratos-admin
|
||||
/cmd/kratos-admin/kratos-admin
|
||||
|
||||
# Develop tools
|
||||
.vscode/
|
||||
|
|
@ -41,3 +41,4 @@ bin/
|
|||
|
||||
web/dist/
|
||||
web/node_modules/
|
||||
.pnpm-store/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"kra/internal/conf"
|
||||
"kra/internal/server"
|
||||
|
||||
"github.com/go-kratos/kratos/contrib/otel/v3/tracing"
|
||||
"github.com/go-kratos/kratos/v3"
|
||||
"github.com/go-kratos/kratos/v3/config"
|
||||
"github.com/go-kratos/kratos/v3/config/file"
|
||||
"github.com/go-kratos/kratos/v3/log"
|
||||
"github.com/go-kratos/kratos/v3/transport/grpc"
|
||||
|
||||
_ "go.uber.org/automaxprocs"
|
||||
)
|
||||
|
||||
// go build -ldflags "-X main.Version=x.y.z"
|
||||
var (
|
||||
// Name is the name of the compiled software.
|
||||
Name string
|
||||
// Version is the version of the compiled software.
|
||||
Version string
|
||||
// flagconf is the config flag.
|
||||
flagconf string
|
||||
|
||||
id, _ = os.Hostname()
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
|
||||
}
|
||||
|
||||
func newApp(logger *slog.Logger, gs *grpc.Server, hs *server.GinServer, scheduler *server.TaskScheduler) *kratos.App {
|
||||
return kratos.New(
|
||||
kratos.ID(id),
|
||||
kratos.Name(Name),
|
||||
kratos.Version(Version),
|
||||
kratos.Metadata(map[string]string{}),
|
||||
kratos.Logger(logger),
|
||||
kratos.Server(
|
||||
gs,
|
||||
hs,
|
||||
scheduler,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
logger := log.NewLogger(
|
||||
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelInfo,
|
||||
}),
|
||||
log.WithExtractor(tracing.TraceAttrs),
|
||||
).With(
|
||||
slog.String("service.id", id),
|
||||
slog.String("service.name", Name),
|
||||
slog.String("service.version", Version),
|
||||
)
|
||||
log.SetDefault(logger)
|
||||
c := config.New(
|
||||
config.WithSource(
|
||||
file.NewSource(flagconf),
|
||||
),
|
||||
)
|
||||
defer c.Close()
|
||||
|
||||
if err := c.Load(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var bc conf.Bootstrap
|
||||
if err := c.Scan(&bc); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if bc.Admin != nil {
|
||||
bc.Admin.ConfigPath = flagconf
|
||||
if info, err := os.Stat(flagconf); err == nil && info.IsDir() {
|
||||
bc.Admin.ConfigPath = filepath.Join(flagconf, "config.yaml")
|
||||
}
|
||||
}
|
||||
|
||||
app, cleanup, err := wireApp(bc.Server, bc.Data, bc.Admin, logger)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// start and wait for stop signal
|
||||
if err := app.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
//go:build wireinject
|
||||
// +build wireinject
|
||||
|
||||
// The build tag makes sure the stub is not built in the final build.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/data"
|
||||
"kra/internal/server"
|
||||
"kra/internal/service"
|
||||
|
||||
"github.com/go-kratos/kratos/v3"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(*conf.Server, *conf.Data, *conf.AdminBackend, *slog.Logger) (*kratos.App, func(), error) {
|
||||
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-kratos/kratos/v3"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/data"
|
||||
"kra/internal/server"
|
||||
"kra/internal/service"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
import (
|
||||
_ "go.uber.org/automaxprocs"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// wireApp init kratos application.
|
||||
func wireApp(confServer *conf.Server, confData *conf.Data, adminBackend *conf.AdminBackend, logger *slog.Logger) (*kratos.App, func(), error) {
|
||||
dataData, cleanup, err := data.NewData(confData, adminBackend)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
adminRepo := data.NewAdminRepo(dataData)
|
||||
adminUsecase := biz.NewAdminUsecase(adminRepo)
|
||||
adminService := service.NewAdminService(adminUsecase)
|
||||
grpcServer := server.NewGRPCServer(confServer, adminService)
|
||||
systemRepo := data.NewSystemRepo(dataData)
|
||||
cache := data.NewCache(dataData)
|
||||
fileStorage, err := data.NewFileStorage(adminBackend)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
systemUsecase := biz.NewSystemUsecase(systemRepo, cache, fileStorage)
|
||||
settingsRepo := data.NewSettingsRepo(dataData)
|
||||
settingsUsecase := biz.NewSettingsUsecase(settingsRepo)
|
||||
settingsService := service.NewSettingsService(settingsUsecase, adminBackend)
|
||||
systemService := service.NewSystemService(systemUsecase, adminBackend, settingsService)
|
||||
accessRepo := data.NewAccessRepo(dataData)
|
||||
accessUsecase := biz.NewAccessUsecase(accessRepo)
|
||||
accessService := service.NewAccessService(accessUsecase)
|
||||
versionRepo := data.NewVersionRepo(dataData)
|
||||
versionUsecase := biz.NewVersionUsecase(versionRepo)
|
||||
versionService := service.NewVersionService(versionUsecase)
|
||||
exportRepo := data.NewExportRepo(dataData)
|
||||
exportUsecase := biz.NewExportUsecase(exportRepo)
|
||||
exportService := service.NewExportService(exportUsecase)
|
||||
auditRepo := data.NewAuditRepo(dataData)
|
||||
auditUsecase := biz.NewAuditUsecase(auditRepo)
|
||||
auditService := service.NewAuditService(auditUsecase)
|
||||
taskRepo := data.NewTaskRepo(dataData)
|
||||
taskUsecase := biz.NewTaskUsecase(taskRepo)
|
||||
taskService := service.NewTaskService(taskUsecase)
|
||||
mediaRepo := data.NewMediaRepo(dataData)
|
||||
mediaUsecase := biz.NewMediaUsecase(mediaRepo, fileStorage)
|
||||
mediaService := service.NewMediaService(mediaUsecase)
|
||||
announcementRepo := data.NewAnnouncementRepo(dataData)
|
||||
announcementUsecase := biz.NewAnnouncementUsecase(announcementRepo)
|
||||
announcementService := service.NewAnnouncementService(announcementUsecase)
|
||||
emailRepo := data.NewEmailRepo(adminBackend)
|
||||
emailUsecase := biz.NewEmailUsecase(emailRepo)
|
||||
emailService := service.NewEmailService(emailUsecase)
|
||||
taskScheduler := server.NewTaskScheduler(taskService, logger)
|
||||
ginServer := server.NewGinServer(confServer, adminBackend, systemService, accessService, settingsService, versionService, exportService, auditService, taskService, mediaService, announcementService, emailService, taskScheduler, logger)
|
||||
app := newApp(logger, grpcServer, ginServer, taskScheduler)
|
||||
return app, func() {
|
||||
cleanup()
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -9,6 +9,12 @@ data:
|
|||
database:
|
||||
driver: mysql
|
||||
source: root:root@tcp(127.0.0.1:3306)/test?timeout=5s&parseTime=True&loc=Local&charset=utf8mb4
|
||||
host: 127.0.0.1
|
||||
port: "3306"
|
||||
user: root
|
||||
password: root
|
||||
name: test
|
||||
config: timeout=5s&parseTime=True&loc=Local&charset=utf8mb4
|
||||
redis:
|
||||
addr: 127.0.0.1:6379
|
||||
read_timeout: 0.2s
|
||||
|
|
@ -18,17 +24,21 @@ admin:
|
|||
jwt:
|
||||
# Production deployments must override this value with a private secret.
|
||||
signing_key: change-me-before-production
|
||||
expires_time: 168h
|
||||
buffer_time: 24h
|
||||
expires_time: 604800s
|
||||
buffer_time: 86400s
|
||||
issuer: kra
|
||||
captcha:
|
||||
key_long: 6
|
||||
img_width: 240
|
||||
img_height: 80
|
||||
store_expiration: 3m
|
||||
store_expiration: 180s
|
||||
local:
|
||||
store_path: uploads/file
|
||||
path_prefix: uploads/file
|
||||
storage:
|
||||
# local, qiniu, aliyun-oss, huawei-obs, tencent-cos, aws-s3,
|
||||
# cloudflare-r2 or minio
|
||||
type: local
|
||||
email:
|
||||
# Leave host/from/secret empty to disable SMTP error notifications.
|
||||
to: ""
|
||||
|
|
|
|||
52
go.mod
52
go.mod
|
|
@ -5,17 +5,20 @@ go 1.25.7
|
|||
require (
|
||||
entgo.io/ent v0.14.6
|
||||
github.com/casbin/casbin/v3 v3.10.0
|
||||
github.com/dzwvip/gorm-oracle v0.1.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-kratos/kratos/contrib/otel/v3 v3.0.0-20260617100506-4e232a3eff59
|
||||
github.com/go-kratos/kratos/v3 v3.0.0
|
||||
github.com/go-sql-driver/mysql v1.10.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/google/wire v0.7.0
|
||||
github.com/minio/minio-go/v7 v7.0.92
|
||||
github.com/mojocn/base64Captcha v1.3.8
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.7
|
||||
github.com/xuri/excelize/v2 v2.9.0
|
||||
go.einride.tech/aip v0.86.3
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
|
|
@ -23,7 +26,10 @@ require (
|
|||
google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324
|
||||
google.golang.org/grpc v1.81.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.6.0
|
||||
gorm.io/driver/postgres v1.6.0
|
||||
gorm.io/driver/sqlserver v1.6.3
|
||||
gorm.io/gorm v1.31.1
|
||||
)
|
||||
|
||||
|
|
@ -32,7 +38,9 @@ replace github.com/go-kratos/kratos/v3 v3.0.0 => github.com/go-kratos/kratos/v3
|
|||
require (
|
||||
ariga.io/atlas v1.2.2 // indirect
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
|
||||
|
|
@ -43,39 +51,69 @@ require (
|
|||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.8.4 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gammazero/toposort v0.1.1 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/inflect v0.21.6 // indirect
|
||||
github.com/go-playground/form/v4 v4.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.10.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.6.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.8.2 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.4 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.7.17 // indirect
|
||||
github.com/thoas/go-funk v0.7.0 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
|
||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zclconf/go-cty v1.18.1 // indirect
|
||||
github.com/zclconf/go-cty-yaml v1.2.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
|
|
@ -91,5 +129,9 @@ require (
|
|||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/tools v0.46.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/fileutil v1.0.0 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.23.1 // indirect
|
||||
)
|
||||
|
|
|
|||
226
go.sum
226
go.sum
|
|
@ -6,10 +6,32 @@ entgo.io/ent v0.14.6 h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk=
|
|||
entgo.io/ent v0.14.6/go.mod h1:z46QBUdGC+BATwsedbDuREfSS0oSCV+csdEYlL4p73s=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
|
||||
|
|
@ -36,11 +58,23 @@ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
|||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dzwvip/gorm-oracle v0.1.2 h1:811aFDY7oDfKWHc0Z0lHdXzzr89EmKBSwc/jLJ8GU5g=
|
||||
github.com/dzwvip/gorm-oracle v0.1.2/go.mod h1:TbF7idnO9UgGpJ0qJpDZby1/wGquzP5GYof88ScBITE=
|
||||
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
||||
|
|
@ -50,12 +84,18 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5
|
|||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gammazero/toposort v0.1.1 h1:OivGxsWxF3U3+U80VoLJ+f50HcPU1MIqE1JlKzoJ2Eg=
|
||||
github.com/gammazero/toposort v0.1.1/go.mod h1:H2cozTnNpMw0hg2VHAYsAxmkHXBYroNangj2NTBQDvw=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588 h1:e0dWyNWFeTgGCH7cRMROahTwMaQYtmHce/6fxVmA6yI=
|
||||
github.com/go-kratos/aip-go/ents v0.0.0-20251213081434-74ffa1fc1588/go.mod h1:ifKMm4eJmaQz5WNKKhfClpCng8UxUB3sArof8wjwG78=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kratos/kratos/contrib/otel/v3 v3.0.0-20260617100506-4e232a3eff59 h1:FeDujUZF6a2pPS0RWGI52IXWPqVcGW1QP5vHDlAqLE8=
|
||||
github.com/go-kratos/kratos/contrib/otel/v3 v3.0.0-20260617100506-4e232a3eff59/go.mod h1:hT2QNZ/0DPOvRqVqxoBBL/aMCOHhbbWh92mF87DBiCU=
|
||||
github.com/go-kratos/kratos/v3 v3.0.0-20260617100506-4e232a3eff59 h1:32laNndL0GsYLuGwWquB6if8yrBQWEzXlkkXImcfeKA=
|
||||
|
|
@ -65,66 +105,128 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
|||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/inflect v0.21.6 h1:0Se5BlyDT4hnV9JQQKA63W9TIUU6SC4hPJiY3qJOEzQ=
|
||||
github.com/go-openapi/inflect v0.21.6/go.mod h1:ksYcnLD7j24H79hdqOMmWaLXjFXd0LTkRoBA0UazLW8=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/form/v4 v4.3.0 h1:OVttojbQv2WNCs4P+VnjPtrt/+30Ipw4890W3OaFlvk=
|
||||
github.com/go-playground/form/v4 v4.3.0/go.mod h1:Cpe1iYJKoXb1vILRXEwxpWMGWyQuqplQ/4cvPecy+Jo=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
|
||||
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
|
||||
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
|
||||
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.92 h1:jpBFWyRS3p8P/9tsRc+NuvqoFi7qAmTCFPoRFmobbVw=
|
||||
github.com/minio/minio-go/v7 v7.0.92/go.mod h1:vTIc8DNcnAZIhyFsk8EB90AbPjj3j68aWIEQCiPj7d0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -132,20 +234,37 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mojocn/base64Captcha v1.3.8 h1:rrN9BhCwXKS8ht1e21kvR3iTaMgf4qPC9sRoV52bqEg=
|
||||
github.com/mojocn/base64Captcha v1.3.8/go.mod h1:QFZy927L8HVP3+VV5z2b1EAEiv1KxVJKZbAucVgLUy4=
|
||||
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2 h1:URwgZpxySdiwu2yQpHk93X4LXWHyFRp1x3Vmlk/YWvo=
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2/go.mod h1:dmKtJ2ahhPWFVi9o1D5GemmWoh/ctuB9peqTowyTO8o=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
|
|
@ -153,13 +272,25 @@ github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM
|
|||
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
|
||||
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
|
||||
github.com/sijms/go-ora/v2 v2.7.17 h1:M/pYIqjaMUeBxyzOWp2oj4ntF6fHSBloJWGNH9vbmsU=
|
||||
github.com/sijms/go-ora/v2 v2.7.17/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
|
|
@ -168,6 +299,14 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
|||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y=
|
||||
github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
|
|
@ -179,6 +318,8 @@ github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmji
|
|||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
|
||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zclconf/go-cty v1.18.1 h1:yEGE8M4iIZlyKQURZNb2SnEyZlZHUcBCnx6KF81KuwM=
|
||||
github.com/zclconf/go-cty v1.18.1/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
|
|
@ -205,29 +346,48 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
|
|||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -236,38 +396,64 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
|
|
@ -280,6 +466,7 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
|
|||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto v0.0.0-20240711142825-46eb208f015d h1:/hmn0Ku5kWij/kjGsrcJeC1T/MrJi2iNWwgAqrihFwc=
|
||||
|
|
@ -293,16 +480,39 @@ google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zN
|
|||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
|
||||
gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
|
||||
gorm.io/driver/sqlserver v1.6.3 h1:UR+nWCuphPnq7UxnL57PSrlYjuvs+sf1N59GgFX7uAI=
|
||||
gorm.io/driver/sqlserver v1.6.3/go.mod h1:VZeNn7hqX1aXoN5TPAFGWvxWG90xtA8erGn2gQmpc6U=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
modernc.org/fileutil v1.0.0 h1:Z1AFLZwl6BO8A5NldQg/xTSjGLetp+1Ubvl4alfGx8w=
|
||||
modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ type UserListFilter struct {
|
|||
Desc bool
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Driver, Host, Port, User, Password, Name, Path, Config, AdminPassword string
|
||||
}
|
||||
|
||||
type Authority struct {
|
||||
AuthorityID uint
|
||||
AuthorityName string
|
||||
|
|
@ -64,7 +68,7 @@ type Menu struct {
|
|||
|
||||
type SystemRepo interface {
|
||||
IsInitialized(context.Context) (bool, error)
|
||||
Initialize(context.Context) error
|
||||
Initialize(context.Context, *DatabaseConfig) error
|
||||
FindUserByUsername(context.Context, string) (*User, error)
|
||||
FindUserByID(context.Context, uint) (*User, error)
|
||||
MenusByAuthority(context.Context, uint) ([]*Menu, error)
|
||||
|
|
@ -72,11 +76,12 @@ type SystemRepo interface {
|
|||
CreateUser(context.Context, *User) (*User, error)
|
||||
UpdateUser(context.Context, *User) error
|
||||
DeleteUser(context.Context, uint) error
|
||||
UpdatePassword(context.Context, uint, string) error
|
||||
UpdatePassword(context.Context, uint, string, bool) error
|
||||
SetUserAuthorities(context.Context, uint, []uint) error
|
||||
SetUserAuthority(context.Context, uint, uint) error
|
||||
SetUserSetting(context.Context, uint, map[string]any) error
|
||||
ListAuthorities(context.Context) ([]*Authority, error)
|
||||
PersistConfig(context.Context) error
|
||||
}
|
||||
|
||||
type SystemUsecase struct {
|
||||
|
|
@ -102,7 +107,13 @@ func (uc *SystemUsecase) CacheDelete(ctx context.Context, key string) error {
|
|||
func (uc *SystemUsecase) IsInitialized(ctx context.Context) (bool, error) {
|
||||
return uc.repo.IsInitialized(ctx)
|
||||
}
|
||||
func (uc *SystemUsecase) Initialize(ctx context.Context) error { return uc.repo.Initialize(ctx) }
|
||||
func (uc *SystemUsecase) Initialize(ctx context.Context, config *DatabaseConfig) error {
|
||||
if config == nil || len(config.AdminPassword) < 6 {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
return uc.repo.Initialize(ctx, config)
|
||||
}
|
||||
func (uc *SystemUsecase) PersistConfig(ctx context.Context) error { return uc.repo.PersistConfig(ctx) }
|
||||
|
||||
func (uc *SystemUsecase) Login(ctx context.Context, username, password string) (*User, error) {
|
||||
u, err := uc.repo.FindUserByUsername(ctx, username)
|
||||
|
|
@ -142,7 +153,7 @@ func (uc *SystemUsecase) ResetPassword(ctx context.Context, id uint, password st
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return uc.repo.UpdatePassword(ctx, id, string(hash))
|
||||
return uc.repo.UpdatePassword(ctx, id, string(hash), false)
|
||||
}
|
||||
func (uc *SystemUsecase) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error {
|
||||
user, err := uc.repo.FindUserByID(ctx, id)
|
||||
|
|
@ -152,7 +163,11 @@ func (uc *SystemUsecase) ChangePassword(ctx context.Context, id uint, oldPasswor
|
|||
if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(oldPassword)) != nil {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
return uc.ResetPassword(ctx, id, newPassword)
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return uc.repo.UpdatePassword(ctx, id, string(hash), true)
|
||||
}
|
||||
func (uc *SystemUsecase) Authorities(ctx context.Context) ([]*Authority, error) {
|
||||
return uc.repo.ListAuthorities(ctx)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.35.1
|
||||
// protoc (unknown)
|
||||
// source: conf/conf.proto
|
||||
|
||||
package conf
|
||||
|
|
@ -188,12 +188,15 @@ func (x *Data) GetRedis() *Data_Redis {
|
|||
|
||||
// AdminBackend contains settings for the administration HTTP transport.
|
||||
type AdminBackend struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RouterPrefix string `protobuf:"bytes,1,opt,name=router_prefix,json=routerPrefix,proto3" json:"router_prefix,omitempty"`
|
||||
Jwt *AdminBackend_JWT `protobuf:"bytes,2,opt,name=jwt,proto3" json:"jwt,omitempty"`
|
||||
Captcha *AdminBackend_Captcha `protobuf:"bytes,3,opt,name=captcha,proto3" json:"captcha,omitempty"`
|
||||
Local *AdminBackend_Local `protobuf:"bytes,4,opt,name=local,proto3" json:"local,omitempty"`
|
||||
Email *AdminBackend_Email `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RouterPrefix string `protobuf:"bytes,1,opt,name=router_prefix,json=routerPrefix,proto3" json:"router_prefix,omitempty"`
|
||||
Jwt *AdminBackend_JWT `protobuf:"bytes,2,opt,name=jwt,proto3" json:"jwt,omitempty"`
|
||||
Captcha *AdminBackend_Captcha `protobuf:"bytes,3,opt,name=captcha,proto3" json:"captcha,omitempty"`
|
||||
Local *AdminBackend_Local `protobuf:"bytes,4,opt,name=local,proto3" json:"local,omitempty"`
|
||||
Email *AdminBackend_Email `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Storage *AdminBackend_Storage `protobuf:"bytes,6,opt,name=storage,proto3" json:"storage,omitempty"`
|
||||
// ConfigPath is populated by the entrypoint and is not required in YAML.
|
||||
ConfigPath string `protobuf:"bytes,7,opt,name=config_path,json=configPath,proto3" json:"config_path,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -263,6 +266,20 @@ func (x *AdminBackend) GetEmail() *AdminBackend_Email {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend) GetStorage() *AdminBackend_Storage {
|
||||
if x != nil {
|
||||
return x.Storage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend) GetConfigPath() string {
|
||||
if x != nil {
|
||||
return x.ConfigPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Server_HTTP struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"`
|
||||
|
|
@ -387,6 +404,13 @@ type Data_Database struct {
|
|||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Driver string `protobuf:"bytes,1,opt,name=driver,proto3" json:"driver,omitempty"`
|
||||
Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"`
|
||||
Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"`
|
||||
Port string `protobuf:"bytes,4,opt,name=port,proto3" json:"port,omitempty"`
|
||||
User string `protobuf:"bytes,5,opt,name=user,proto3" json:"user,omitempty"`
|
||||
Password string `protobuf:"bytes,6,opt,name=password,proto3" json:"password,omitempty"`
|
||||
Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"`
|
||||
Config string `protobuf:"bytes,8,opt,name=config,proto3" json:"config,omitempty"`
|
||||
Path string `protobuf:"bytes,9,opt,name=path,proto3" json:"path,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
|
@ -435,6 +459,55 @@ func (x *Data_Database) GetSource() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetPort() string {
|
||||
if x != nil {
|
||||
return x.Port
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetUser() string {
|
||||
if x != nil {
|
||||
return x.User
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetConfig() string {
|
||||
if x != nil {
|
||||
return x.Config
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Data_Database) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Data_Redis struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"`
|
||||
|
|
@ -791,6 +864,316 @@ func (x *AdminBackend_Email) GetIsLoginAuth() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
type AdminBackend_Storage struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
Qiniu *AdminBackend_Qiniu `protobuf:"bytes,2,opt,name=qiniu,proto3" json:"qiniu,omitempty"`
|
||||
AliyunOss *AdminBackend_ObjectStore `protobuf:"bytes,3,opt,name=aliyun_oss,json=aliyunOss,proto3" json:"aliyun_oss,omitempty"`
|
||||
HuaweiObs *AdminBackend_ObjectStore `protobuf:"bytes,4,opt,name=huawei_obs,json=huaweiObs,proto3" json:"huawei_obs,omitempty"`
|
||||
TencentCos *AdminBackend_ObjectStore `protobuf:"bytes,5,opt,name=tencent_cos,json=tencentCos,proto3" json:"tencent_cos,omitempty"`
|
||||
AwsS3 *AdminBackend_ObjectStore `protobuf:"bytes,6,opt,name=aws_s3,json=awsS3,proto3" json:"aws_s3,omitempty"`
|
||||
CloudflareR2 *AdminBackend_ObjectStore `protobuf:"bytes,7,opt,name=cloudflare_r2,json=cloudflareR2,proto3" json:"cloudflare_r2,omitempty"`
|
||||
Minio *AdminBackend_ObjectStore `protobuf:"bytes,8,opt,name=minio,proto3" json:"minio,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) Reset() {
|
||||
*x = AdminBackend_Storage{}
|
||||
mi := &file_conf_conf_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AdminBackend_Storage) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_Storage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AdminBackend_Storage.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_Storage) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 4}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetType() string {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetQiniu() *AdminBackend_Qiniu {
|
||||
if x != nil {
|
||||
return x.Qiniu
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetAliyunOss() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.AliyunOss
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetHuaweiObs() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.HuaweiObs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetTencentCos() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.TencentCos
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetAwsS3() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.AwsS3
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetCloudflareR2() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.CloudflareR2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetMinio() *AdminBackend_ObjectStore {
|
||||
if x != nil {
|
||||
return x.Minio
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AdminBackend_Qiniu struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Zone string `protobuf:"bytes,1,opt,name=zone,proto3" json:"zone,omitempty"`
|
||||
Bucket string `protobuf:"bytes,2,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
||||
BaseUrl string `protobuf:"bytes,3,opt,name=base_url,json=baseUrl,proto3" json:"base_url,omitempty"`
|
||||
AccessKey string `protobuf:"bytes,4,opt,name=access_key,json=accessKey,proto3" json:"access_key,omitempty"`
|
||||
SecretKey string `protobuf:"bytes,5,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"`
|
||||
UseHttps bool `protobuf:"varint,6,opt,name=use_https,json=useHttps,proto3" json:"use_https,omitempty"`
|
||||
UseCdnDomains bool `protobuf:"varint,7,opt,name=use_cdn_domains,json=useCdnDomains,proto3" json:"use_cdn_domains,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) Reset() {
|
||||
*x = AdminBackend_Qiniu{}
|
||||
mi := &file_conf_conf_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AdminBackend_Qiniu) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_Qiniu) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AdminBackend_Qiniu.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_Qiniu) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 5}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetZone() string {
|
||||
if x != nil {
|
||||
return x.Zone
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetBucket() string {
|
||||
if x != nil {
|
||||
return x.Bucket
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetBaseUrl() string {
|
||||
if x != nil {
|
||||
return x.BaseUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetAccessKey() string {
|
||||
if x != nil {
|
||||
return x.AccessKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetSecretKey() string {
|
||||
if x != nil {
|
||||
return x.SecretKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetUseHttps() bool {
|
||||
if x != nil {
|
||||
return x.UseHttps
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetUseCdnDomains() bool {
|
||||
if x != nil {
|
||||
return x.UseCdnDomains
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ObjectStore covers S3-compatible configuration used by public cloud
|
||||
// providers and self-hosted MinIO.
|
||||
type AdminBackend_ObjectStore struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||
Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"`
|
||||
Bucket string `protobuf:"bytes,3,opt,name=bucket,proto3" json:"bucket,omitempty"`
|
||||
AccessKey string `protobuf:"bytes,4,opt,name=access_key,json=accessKey,proto3" json:"access_key,omitempty"`
|
||||
SecretKey string `protobuf:"bytes,5,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"`
|
||||
BaseUrl string `protobuf:"bytes,6,opt,name=base_url,json=baseUrl,proto3" json:"base_url,omitempty"`
|
||||
PathPrefix string `protobuf:"bytes,7,opt,name=path_prefix,json=pathPrefix,proto3" json:"path_prefix,omitempty"`
|
||||
UseSsl bool `protobuf:"varint,8,opt,name=use_ssl,json=useSsl,proto3" json:"use_ssl,omitempty"`
|
||||
ForcePathStyle bool `protobuf:"varint,9,opt,name=force_path_style,json=forcePathStyle,proto3" json:"force_path_style,omitempty"`
|
||||
AccountId string `protobuf:"bytes,10,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) Reset() {
|
||||
*x = AdminBackend_ObjectStore{}
|
||||
mi := &file_conf_conf_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AdminBackend_ObjectStore) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AdminBackend_ObjectStore.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_ObjectStore) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 6}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetEndpoint() string {
|
||||
if x != nil {
|
||||
return x.Endpoint
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetRegion() string {
|
||||
if x != nil {
|
||||
return x.Region
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetBucket() string {
|
||||
if x != nil {
|
||||
return x.Bucket
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetAccessKey() string {
|
||||
if x != nil {
|
||||
return x.AccessKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetSecretKey() string {
|
||||
if x != nil {
|
||||
return x.SecretKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetBaseUrl() string {
|
||||
if x != nil {
|
||||
return x.BaseUrl
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetPathPrefix() string {
|
||||
if x != nil {
|
||||
return x.PathPrefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetUseSsl() bool {
|
||||
if x != nil {
|
||||
return x.UseSsl
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetForcePathStyle() bool {
|
||||
if x != nil {
|
||||
return x.ForcePathStyle
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetAccountId() string {
|
||||
if x != nil {
|
||||
return x.AccountId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_conf_conf_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_conf_conf_proto_rawDesc = "" +
|
||||
|
|
@ -811,24 +1194,34 @@ const file_conf_conf_proto_rawDesc = "" +
|
|||
"\x04GRPC\x12\x18\n" +
|
||||
"\anetwork\x18\x01 \x01(\tR\anetwork\x12\x12\n" +
|
||||
"\x04addr\x18\x02 \x01(\tR\x04addr\x123\n" +
|
||||
"\atimeout\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\atimeout\"\xdd\x02\n" +
|
||||
"\atimeout\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\atimeout\"\xf6\x03\n" +
|
||||
"\x04Data\x125\n" +
|
||||
"\bdatabase\x18\x01 \x01(\v2\x19.kratos.api.Data.DatabaseR\bdatabase\x12,\n" +
|
||||
"\x05redis\x18\x02 \x01(\v2\x16.kratos.api.Data.RedisR\x05redis\x1a:\n" +
|
||||
"\x05redis\x18\x02 \x01(\v2\x16.kratos.api.Data.RedisR\x05redis\x1a\xd2\x01\n" +
|
||||
"\bDatabase\x12\x16\n" +
|
||||
"\x06driver\x18\x01 \x01(\tR\x06driver\x12\x16\n" +
|
||||
"\x06source\x18\x02 \x01(\tR\x06source\x1a\xb3\x01\n" +
|
||||
"\x06source\x18\x02 \x01(\tR\x06source\x12\x12\n" +
|
||||
"\x04host\x18\x03 \x01(\tR\x04host\x12\x12\n" +
|
||||
"\x04port\x18\x04 \x01(\tR\x04port\x12\x12\n" +
|
||||
"\x04user\x18\x05 \x01(\tR\x04user\x12\x1a\n" +
|
||||
"\bpassword\x18\x06 \x01(\tR\bpassword\x12\x12\n" +
|
||||
"\x04name\x18\a \x01(\tR\x04name\x12\x16\n" +
|
||||
"\x06config\x18\b \x01(\tR\x06config\x12\x12\n" +
|
||||
"\x04path\x18\t \x01(\tR\x04path\x1a\xb3\x01\n" +
|
||||
"\x05Redis\x12\x18\n" +
|
||||
"\anetwork\x18\x01 \x01(\tR\anetwork\x12\x12\n" +
|
||||
"\x04addr\x18\x02 \x01(\tR\x04addr\x12<\n" +
|
||||
"\fread_timeout\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\vreadTimeout\x12>\n" +
|
||||
"\rwrite_timeout\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\fwriteTimeout\"\xfd\x06\n" +
|
||||
"\rwrite_timeout\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\fwriteTimeout\"\xd1\x0f\n" +
|
||||
"\fAdminBackend\x12#\n" +
|
||||
"\rrouter_prefix\x18\x01 \x01(\tR\frouterPrefix\x12.\n" +
|
||||
"\x03jwt\x18\x02 \x01(\v2\x1c.kratos.api.AdminBackend.JWTR\x03jwt\x12:\n" +
|
||||
"\acaptcha\x18\x03 \x01(\v2 .kratos.api.AdminBackend.CaptchaR\acaptcha\x124\n" +
|
||||
"\x05local\x18\x04 \x01(\v2\x1e.kratos.api.AdminBackend.LocalR\x05local\x124\n" +
|
||||
"\x05email\x18\x05 \x01(\v2\x1e.kratos.api.AdminBackend.EmailR\x05email\x1a\xb8\x01\n" +
|
||||
"\x05email\x18\x05 \x01(\v2\x1e.kratos.api.AdminBackend.EmailR\x05email\x12:\n" +
|
||||
"\astorage\x18\x06 \x01(\v2 .kratos.api.AdminBackend.StorageR\astorage\x12\x1f\n" +
|
||||
"\vconfig_path\x18\a \x01(\tR\n" +
|
||||
"configPath\x1a\xb8\x01\n" +
|
||||
"\x03JWT\x12\x1f\n" +
|
||||
"\vsigning_key\x18\x01 \x01(\tR\n" +
|
||||
"signingKey\x12<\n" +
|
||||
|
|
@ -855,7 +1248,45 @@ const file_conf_conf_proto_rawDesc = "" +
|
|||
"\bnickname\x18\x05 \x01(\tR\bnickname\x12\x12\n" +
|
||||
"\x04port\x18\x06 \x01(\x05R\x04port\x12\x15\n" +
|
||||
"\x06is_ssl\x18\a \x01(\bR\x05isSsl\x12\"\n" +
|
||||
"\ris_login_auth\x18\b \x01(\bR\visLoginAuthB\x18Z\x16kra/internal/conf;confb\x06proto3"
|
||||
"\ris_login_auth\x18\b \x01(\bR\visLoginAuth\x1a\xe8\x03\n" +
|
||||
"\aStorage\x12\x12\n" +
|
||||
"\x04type\x18\x01 \x01(\tR\x04type\x124\n" +
|
||||
"\x05qiniu\x18\x02 \x01(\v2\x1e.kratos.api.AdminBackend.QiniuR\x05qiniu\x12C\n" +
|
||||
"\n" +
|
||||
"aliyun_oss\x18\x03 \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\taliyunOss\x12C\n" +
|
||||
"\n" +
|
||||
"huawei_obs\x18\x04 \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\thuaweiObs\x12E\n" +
|
||||
"\vtencent_cos\x18\x05 \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\n" +
|
||||
"tencentCos\x12;\n" +
|
||||
"\x06aws_s3\x18\x06 \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\x05awsS3\x12I\n" +
|
||||
"\rcloudflare_r2\x18\a \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\fcloudflareR2\x12:\n" +
|
||||
"\x05minio\x18\b \x01(\v2$.kratos.api.AdminBackend.ObjectStoreR\x05minio\x1a\xd1\x01\n" +
|
||||
"\x05Qiniu\x12\x12\n" +
|
||||
"\x04zone\x18\x01 \x01(\tR\x04zone\x12\x16\n" +
|
||||
"\x06bucket\x18\x02 \x01(\tR\x06bucket\x12\x19\n" +
|
||||
"\bbase_url\x18\x03 \x01(\tR\abaseUrl\x12\x1d\n" +
|
||||
"\n" +
|
||||
"access_key\x18\x04 \x01(\tR\taccessKey\x12\x1d\n" +
|
||||
"\n" +
|
||||
"secret_key\x18\x05 \x01(\tR\tsecretKey\x12\x1b\n" +
|
||||
"\tuse_https\x18\x06 \x01(\bR\buseHttps\x12&\n" +
|
||||
"\x0fuse_cdn_domains\x18\a \x01(\bR\ruseCdnDomains\x1a\xb5\x02\n" +
|
||||
"\vObjectStore\x12\x1a\n" +
|
||||
"\bendpoint\x18\x01 \x01(\tR\bendpoint\x12\x16\n" +
|
||||
"\x06region\x18\x02 \x01(\tR\x06region\x12\x16\n" +
|
||||
"\x06bucket\x18\x03 \x01(\tR\x06bucket\x12\x1d\n" +
|
||||
"\n" +
|
||||
"access_key\x18\x04 \x01(\tR\taccessKey\x12\x1d\n" +
|
||||
"\n" +
|
||||
"secret_key\x18\x05 \x01(\tR\tsecretKey\x12\x19\n" +
|
||||
"\bbase_url\x18\x06 \x01(\tR\abaseUrl\x12\x1f\n" +
|
||||
"\vpath_prefix\x18\a \x01(\tR\n" +
|
||||
"pathPrefix\x12\x17\n" +
|
||||
"\ause_ssl\x18\b \x01(\bR\x06useSsl\x12(\n" +
|
||||
"\x10force_path_style\x18\t \x01(\bR\x0eforcePathStyle\x12\x1d\n" +
|
||||
"\n" +
|
||||
"account_id\x18\n" +
|
||||
" \x01(\tR\taccountIdB\x18Z\x16kra/internal/conf;confb\x06proto3"
|
||||
|
||||
var (
|
||||
file_conf_conf_proto_rawDescOnce sync.Once
|
||||
|
|
@ -869,21 +1300,24 @@ func file_conf_conf_proto_rawDescGZIP() []byte {
|
|||
return file_conf_conf_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
|
||||
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_conf_conf_proto_goTypes = []any{
|
||||
(*Bootstrap)(nil), // 0: kratos.api.Bootstrap
|
||||
(*Server)(nil), // 1: kratos.api.Server
|
||||
(*Data)(nil), // 2: kratos.api.Data
|
||||
(*AdminBackend)(nil), // 3: kratos.api.AdminBackend
|
||||
(*Server_HTTP)(nil), // 4: kratos.api.Server.HTTP
|
||||
(*Server_GRPC)(nil), // 5: kratos.api.Server.GRPC
|
||||
(*Data_Database)(nil), // 6: kratos.api.Data.Database
|
||||
(*Data_Redis)(nil), // 7: kratos.api.Data.Redis
|
||||
(*AdminBackend_JWT)(nil), // 8: kratos.api.AdminBackend.JWT
|
||||
(*AdminBackend_Captcha)(nil), // 9: kratos.api.AdminBackend.Captcha
|
||||
(*AdminBackend_Local)(nil), // 10: kratos.api.AdminBackend.Local
|
||||
(*AdminBackend_Email)(nil), // 11: kratos.api.AdminBackend.Email
|
||||
(*durationpb.Duration)(nil), // 12: google.protobuf.Duration
|
||||
(*Bootstrap)(nil), // 0: kratos.api.Bootstrap
|
||||
(*Server)(nil), // 1: kratos.api.Server
|
||||
(*Data)(nil), // 2: kratos.api.Data
|
||||
(*AdminBackend)(nil), // 3: kratos.api.AdminBackend
|
||||
(*Server_HTTP)(nil), // 4: kratos.api.Server.HTTP
|
||||
(*Server_GRPC)(nil), // 5: kratos.api.Server.GRPC
|
||||
(*Data_Database)(nil), // 6: kratos.api.Data.Database
|
||||
(*Data_Redis)(nil), // 7: kratos.api.Data.Redis
|
||||
(*AdminBackend_JWT)(nil), // 8: kratos.api.AdminBackend.JWT
|
||||
(*AdminBackend_Captcha)(nil), // 9: kratos.api.AdminBackend.Captcha
|
||||
(*AdminBackend_Local)(nil), // 10: kratos.api.AdminBackend.Local
|
||||
(*AdminBackend_Email)(nil), // 11: kratos.api.AdminBackend.Email
|
||||
(*AdminBackend_Storage)(nil), // 12: kratos.api.AdminBackend.Storage
|
||||
(*AdminBackend_Qiniu)(nil), // 13: kratos.api.AdminBackend.Qiniu
|
||||
(*AdminBackend_ObjectStore)(nil), // 14: kratos.api.AdminBackend.ObjectStore
|
||||
(*durationpb.Duration)(nil), // 15: google.protobuf.Duration
|
||||
}
|
||||
var file_conf_conf_proto_depIdxs = []int32{
|
||||
1, // 0: kratos.api.Bootstrap.server:type_name -> kratos.api.Server
|
||||
|
|
@ -897,18 +1331,26 @@ var file_conf_conf_proto_depIdxs = []int32{
|
|||
9, // 8: kratos.api.AdminBackend.captcha:type_name -> kratos.api.AdminBackend.Captcha
|
||||
10, // 9: kratos.api.AdminBackend.local:type_name -> kratos.api.AdminBackend.Local
|
||||
11, // 10: kratos.api.AdminBackend.email:type_name -> kratos.api.AdminBackend.Email
|
||||
12, // 11: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration
|
||||
12, // 12: kratos.api.Server.GRPC.timeout:type_name -> google.protobuf.Duration
|
||||
12, // 13: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration
|
||||
12, // 14: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration
|
||||
12, // 15: kratos.api.AdminBackend.JWT.expires_time:type_name -> google.protobuf.Duration
|
||||
12, // 16: kratos.api.AdminBackend.JWT.buffer_time:type_name -> google.protobuf.Duration
|
||||
12, // 17: kratos.api.AdminBackend.Captcha.store_expiration:type_name -> google.protobuf.Duration
|
||||
18, // [18:18] is the sub-list for method output_type
|
||||
18, // [18:18] is the sub-list for method input_type
|
||||
18, // [18:18] is the sub-list for extension type_name
|
||||
18, // [18:18] is the sub-list for extension extendee
|
||||
0, // [0:18] is the sub-list for field type_name
|
||||
12, // 11: kratos.api.AdminBackend.storage:type_name -> kratos.api.AdminBackend.Storage
|
||||
15, // 12: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration
|
||||
15, // 13: kratos.api.Server.GRPC.timeout:type_name -> google.protobuf.Duration
|
||||
15, // 14: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration
|
||||
15, // 15: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration
|
||||
15, // 16: kratos.api.AdminBackend.JWT.expires_time:type_name -> google.protobuf.Duration
|
||||
15, // 17: kratos.api.AdminBackend.JWT.buffer_time:type_name -> google.protobuf.Duration
|
||||
15, // 18: kratos.api.AdminBackend.Captcha.store_expiration:type_name -> google.protobuf.Duration
|
||||
13, // 19: kratos.api.AdminBackend.Storage.qiniu:type_name -> kratos.api.AdminBackend.Qiniu
|
||||
14, // 20: kratos.api.AdminBackend.Storage.aliyun_oss:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
14, // 21: kratos.api.AdminBackend.Storage.huawei_obs:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
14, // 22: kratos.api.AdminBackend.Storage.tencent_cos:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
14, // 23: kratos.api.AdminBackend.Storage.aws_s3:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
14, // 24: kratos.api.AdminBackend.Storage.cloudflare_r2:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
14, // 25: kratos.api.AdminBackend.Storage.minio:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
26, // [26:26] is the sub-list for method output_type
|
||||
26, // [26:26] is the sub-list for method input_type
|
||||
26, // [26:26] is the sub-list for extension type_name
|
||||
26, // [26:26] is the sub-list for extension extendee
|
||||
0, // [0:26] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_conf_conf_proto_init() }
|
||||
|
|
@ -922,7 +1364,7 @@ func file_conf_conf_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_conf_conf_proto_rawDesc), len(file_conf_conf_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 12,
|
||||
NumMessages: 15,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ message Data {
|
|||
message Database {
|
||||
string driver = 1;
|
||||
string source = 2;
|
||||
string host = 3;
|
||||
string port = 4;
|
||||
string user = 5;
|
||||
string password = 6;
|
||||
string name = 7;
|
||||
string config = 8;
|
||||
string path = 9;
|
||||
}
|
||||
message Redis {
|
||||
string network = 1;
|
||||
|
|
@ -48,6 +55,9 @@ message AdminBackend {
|
|||
Captcha captcha = 3;
|
||||
Local local = 4;
|
||||
Email email = 5;
|
||||
Storage storage = 6;
|
||||
// ConfigPath is populated by the entrypoint and is not required in YAML.
|
||||
string config_path = 7;
|
||||
|
||||
message JWT {
|
||||
string signing_key = 1;
|
||||
|
|
@ -78,4 +88,40 @@ message AdminBackend {
|
|||
bool is_ssl = 7;
|
||||
bool is_login_auth = 8;
|
||||
}
|
||||
|
||||
message Storage {
|
||||
string type = 1;
|
||||
Qiniu qiniu = 2;
|
||||
ObjectStore aliyun_oss = 3;
|
||||
ObjectStore huawei_obs = 4;
|
||||
ObjectStore tencent_cos = 5;
|
||||
ObjectStore aws_s3 = 6;
|
||||
ObjectStore cloudflare_r2 = 7;
|
||||
ObjectStore minio = 8;
|
||||
}
|
||||
|
||||
message Qiniu {
|
||||
string zone = 1;
|
||||
string bucket = 2;
|
||||
string base_url = 3;
|
||||
string access_key = 4;
|
||||
string secret_key = 5;
|
||||
bool use_https = 6;
|
||||
bool use_cdn_domains = 7;
|
||||
}
|
||||
|
||||
// ObjectStore covers S3-compatible configuration used by public cloud
|
||||
// providers and self-hosted MinIO.
|
||||
message ObjectStore {
|
||||
string endpoint = 1;
|
||||
string region = 2;
|
||||
string bucket = 3;
|
||||
string access_key = 4;
|
||||
string secret_key = 5;
|
||||
string base_url = 6;
|
||||
string path_prefix = 7;
|
||||
bool use_ssl = 8;
|
||||
bool force_path_style = 9;
|
||||
string account_id = 10;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,126 +2,84 @@ package data
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kratos/aip-go/ents"
|
||||
"gorm.io/gorm"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/data/ent"
|
||||
"kra/internal/data/ent/admin"
|
||||
)
|
||||
|
||||
func convertAdmin(po *ent.Admin) *biz.Admin {
|
||||
return &biz.Admin{
|
||||
ID: po.ID,
|
||||
Name: po.Name,
|
||||
Email: po.Email,
|
||||
Avatar: po.Avatar,
|
||||
Access: po.Access,
|
||||
Password: po.Password,
|
||||
CreateTime: po.CreateTime,
|
||||
UpdateTime: po.UpdateTime,
|
||||
type adminPO struct {
|
||||
ID int64 `gorm:"primaryKey;autoIncrement"`
|
||||
Name string `gorm:"uniqueIndex"`
|
||||
Email string `gorm:"uniqueIndex"`
|
||||
Password string
|
||||
Access string
|
||||
Avatar string
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
}
|
||||
|
||||
func (adminPO) TableName() string { return "admins" }
|
||||
func convertAdmin(po *adminPO) *biz.Admin {
|
||||
return &biz.Admin{ID: po.ID, Name: po.Name, Email: po.Email, Password: po.Password, Access: po.Access, Avatar: po.Avatar, CreateTime: po.CreateTime, UpdateTime: po.UpdateTime}
|
||||
}
|
||||
|
||||
type adminRepo struct{ data *Data }
|
||||
|
||||
func NewAdminRepo(data *Data) biz.AdminRepo { return &adminRepo{data: data} }
|
||||
func (r *adminRepo) one(ctx context.Context, query *gorm.DB) (*biz.Admin, error) {
|
||||
var po adminPO
|
||||
if err := query.WithContext(ctx).First(&po).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, biz.ErrAdminNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(&po), nil
|
||||
}
|
||||
|
||||
type adminRepo struct {
|
||||
data *Data
|
||||
}
|
||||
|
||||
// NewAdminRepo creates a new AdminRepo instance.
|
||||
func NewAdminRepo(data *Data) biz.AdminRepo {
|
||||
return &adminRepo{
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *adminRepo) FindByID(ctx context.Context, id int64) (*biz.Admin, error) {
|
||||
po, err := r.data.db.Admin.Get(ctx, id)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return nil, biz.ErrAdminNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(po), nil
|
||||
return r.one(ctx, r.data.gormDB.Where("id = ?", id))
|
||||
}
|
||||
|
||||
func (r *adminRepo) FindByName(ctx context.Context, name string) (*biz.Admin, error) {
|
||||
po, err := r.data.db.Admin.Query().Where(admin.NameEQ(name)).Only(ctx)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return nil, biz.ErrAdminNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(po), nil
|
||||
return r.one(ctx, r.data.gormDB.Where("name = ?", name))
|
||||
}
|
||||
|
||||
func (r *adminRepo) FindByEmail(ctx context.Context, email string) (*biz.Admin, error) {
|
||||
po, err := r.data.db.Admin.Query().Where(admin.EmailEQ(email)).Only(ctx)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
return nil, biz.ErrAdminNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(po), nil
|
||||
return r.one(ctx, r.data.gormDB.Where("email = ?", email))
|
||||
}
|
||||
|
||||
func (r *adminRepo) ListAdmins(ctx context.Context, opts ...biz.ListOption) ([]*biz.Admin, error) {
|
||||
o := biz.ListOptions{Limit: 20}
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
pos, err := r.data.db.Admin.Query().
|
||||
Where(ents.ApplyFilter(o.Filter)).
|
||||
Order(ents.ApplyOrderBy(o.OrderBy)).
|
||||
Offset(o.Offset).
|
||||
Limit(o.Limit).
|
||||
All(ctx)
|
||||
if err != nil {
|
||||
var pos []adminPO
|
||||
if err := r.data.gormDB.WithContext(ctx).Offset(o.Offset).Limit(o.Limit).Order("id asc").Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var admins []*biz.Admin
|
||||
for _, po := range pos {
|
||||
admins = append(admins, convertAdmin(po))
|
||||
out := make([]*biz.Admin, 0, len(pos))
|
||||
for i := range pos {
|
||||
out = append(out, convertAdmin(&pos[i]))
|
||||
}
|
||||
return admins, nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *adminRepo) CreateAdmin(ctx context.Context, admin *biz.Admin) (*biz.Admin, error) {
|
||||
po, err := r.data.db.Admin.Create().
|
||||
SetName(admin.Name).
|
||||
SetEmail(admin.Email).
|
||||
SetAvatar(admin.Avatar).
|
||||
SetAccess(admin.Access).
|
||||
SetPassword(admin.Password).
|
||||
SetCreateTime(time.Now()).
|
||||
SetUpdateTime(time.Now()).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
func (r *adminRepo) CreateAdmin(ctx context.Context, a *biz.Admin) (*biz.Admin, error) {
|
||||
now := time.Now()
|
||||
po := adminPO{Name: a.Name, Email: a.Email, Password: a.Password, Access: a.Access, Avatar: a.Avatar, CreateTime: now, UpdateTime: now}
|
||||
if err := r.data.gormDB.WithContext(ctx).Create(&po).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(po), nil
|
||||
return convertAdmin(&po), nil
|
||||
}
|
||||
|
||||
func (r *adminRepo) UpdateAdmin(ctx context.Context, admin *biz.Admin) (*biz.Admin, error) {
|
||||
update := r.data.db.Admin.UpdateOneID(admin.ID).
|
||||
SetName(admin.Name).
|
||||
SetEmail(admin.Email).
|
||||
SetAccess(admin.Access).
|
||||
SetAvatar(admin.Avatar).
|
||||
SetUpdateTime(time.Now())
|
||||
// Only update the password if it's not empty
|
||||
if admin.Password != "" {
|
||||
update.SetPassword(admin.Password)
|
||||
func (r *adminRepo) UpdateAdmin(ctx context.Context, a *biz.Admin) (*biz.Admin, error) {
|
||||
values := map[string]any{"name": a.Name, "email": a.Email, "access": a.Access, "avatar": a.Avatar, "update_time": time.Now()}
|
||||
if a.Password != "" {
|
||||
values["password"] = a.Password
|
||||
}
|
||||
po, err := update.Save(ctx)
|
||||
if err != nil {
|
||||
if err := r.data.gormDB.WithContext(ctx).Model(&adminPO{}).Where("id = ?", a.ID).Updates(values).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertAdmin(po), nil
|
||||
return r.FindByID(ctx, a.ID)
|
||||
}
|
||||
|
||||
func (r *adminRepo) DeleteAdmin(ctx context.Context, id int64) error {
|
||||
return r.data.db.Admin.DeleteOneID(id).Exec(ctx)
|
||||
return r.data.gormDB.WithContext(ctx).Delete(&adminPO{}, id).Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func protoMap(message proto.Message) (map[string]any, error) {
|
||||
raw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var value map[string]any
|
||||
if err = json.Unmarshal(raw, &value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(value, "config_path")
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func setYAMLMapping(node *yaml.Node, key string, value any) error {
|
||||
if node.Kind == yaml.DocumentNode {
|
||||
node = node.Content[0]
|
||||
}
|
||||
if node.Kind != yaml.MappingNode {
|
||||
return fmt.Errorf("configuration root is not a mapping")
|
||||
}
|
||||
raw, err := yaml.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var replacement yaml.Node
|
||||
if err = yaml.Unmarshal(raw, &replacement); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < len(node.Content); i += 2 {
|
||||
if node.Content[i].Value == key {
|
||||
node.Content[i+1] = replacement.Content[0]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: key}, replacement.Content[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Data) persistConfig() error {
|
||||
if d.admin == nil || d.admin.ConfigPath == "" {
|
||||
return nil
|
||||
}
|
||||
raw, err := os.ReadFile(d.admin.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var document yaml.Node
|
||||
if err = yaml.Unmarshal(raw, &document); err != nil {
|
||||
return err
|
||||
}
|
||||
dataValue, err := protoMap(d.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminValue, err := protoMap(d.admin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = setYAMLMapping(&document, "data", dataValue); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = setYAMLMapping(&document, "admin", adminValue); err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := yaml.Marshal(&document)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Dir(d.admin.ConfigPath), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
temporary, err := os.CreateTemp(filepath.Dir(d.admin.ConfigPath), ".kra-config-*.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tempName := temporary.Name()
|
||||
defer os.Remove(tempName)
|
||||
if _, err = temporary.Write(output); err != nil {
|
||||
_ = temporary.Close()
|
||||
return err
|
||||
}
|
||||
if err = temporary.Chmod(0o600); err != nil {
|
||||
_ = temporary.Close()
|
||||
return err
|
||||
}
|
||||
if err = temporary.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tempName, d.admin.ConfigPath)
|
||||
}
|
||||
|
|
@ -4,55 +4,47 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/google/wire"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/data/ent"
|
||||
"kra/internal/data/ent/migrate"
|
||||
)
|
||||
|
||||
// ProviderSet is data providers.
|
||||
var ProviderSet = wire.NewSet(NewData, NewAdminRepo, NewSystemRepo, NewAccessRepo, NewSettingsRepo, NewVersionRepo, NewExportRepo, NewAuditRepo, NewTaskRepo, NewMediaRepo, NewAnnouncementRepo, NewEmailRepo, NewCache, NewFileStorage)
|
||||
|
||||
// Data is a struct that contains the database client.
|
||||
type Data struct {
|
||||
db *ent.Client
|
||||
gormDB *gorm.DB
|
||||
redis *redis.Client
|
||||
mu sync.RWMutex
|
||||
gormDB *gorm.DB
|
||||
redis *redis.Client
|
||||
database *conf.Data_Database
|
||||
config *conf.Data
|
||||
admin *conf.AdminBackend
|
||||
}
|
||||
|
||||
// NewData creates a new Data instance.
|
||||
func NewData(c *conf.Data) (*Data, func(), error) {
|
||||
func NewData(c *conf.Data, admin *conf.AdminBackend) (*Data, func(), error) {
|
||||
if c == nil || c.Database == nil {
|
||||
return nil, nil, fmt.Errorf("database configuration is required")
|
||||
}
|
||||
db, err := ent.Open(c.Database.Driver, c.Database.Source)
|
||||
d := &Data{database: c.Database, config: c, admin: admin}
|
||||
db, err := openDatabase(c.Database, false)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("open ent database: %w", err)
|
||||
}
|
||||
gormDB, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{})
|
||||
if err != nil {
|
||||
_ = db.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
// The announcement module owns its table lifecycle independently of the
|
||||
// first-run database initializer.
|
||||
if err = gormDB.AutoMigrate(&announcementPO{}); err != nil {
|
||||
_ = db.Close()
|
||||
return nil, nil, fmt.Errorf("migrate announcement table: %w", err)
|
||||
}
|
||||
var redisClient *redis.Client
|
||||
if c.Redis != nil && c.Redis.Addr != "" {
|
||||
options := &redis.Options{Addr: c.Redis.Addr}
|
||||
if c.Redis.Network != "" {
|
||||
options.Network = c.Redis.Network
|
||||
// The initialization endpoint must remain available when the configured
|
||||
// target database has not been created yet.
|
||||
log.Printf("configured database unavailable before initialization: %v", err)
|
||||
db, err = openFallbackDatabase()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("open bootstrap database: %w", err)
|
||||
}
|
||||
}
|
||||
d.gormDB = db
|
||||
if err = db.AutoMigrate(&announcementPO{}, &adminPO{}); err != nil {
|
||||
return nil, nil, fmt.Errorf("migrate bootstrap tables: %w", err)
|
||||
}
|
||||
if c.Redis != nil && c.Redis.Addr != "" {
|
||||
options := &redis.Options{Addr: c.Redis.Addr, Network: c.Redis.Network}
|
||||
if c.Redis.ReadTimeout != nil {
|
||||
options.ReadTimeout = c.Redis.ReadTimeout.AsDuration()
|
||||
}
|
||||
|
|
@ -65,27 +57,42 @@ func NewData(c *conf.Data) (*Data, func(), error) {
|
|||
log.Printf("redis unavailable, using in-memory cache: %v", pingErr)
|
||||
_ = candidate.Close()
|
||||
} else {
|
||||
redisClient = candidate
|
||||
d.redis = candidate
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
if os.Getenv("DEPLOY_ENV") == "dev" {
|
||||
// Enable debug mode for detailed logging.
|
||||
db = db.Debug()
|
||||
// Run the auto migration tool.
|
||||
if err = db.Schema.Create(context.Background(), migrate.WithDropIndex(true)); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
cleanup := func() {
|
||||
_ = db.Close()
|
||||
if redisClient != nil {
|
||||
_ = redisClient.Close()
|
||||
d.mu.RLock()
|
||||
db := d.gormDB
|
||||
d.mu.RUnlock()
|
||||
if sqlDB, closeErr := db.DB(); closeErr == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
if d.redis != nil {
|
||||
_ = d.redis.Close()
|
||||
}
|
||||
}
|
||||
return &Data{
|
||||
db: db,
|
||||
gormDB: gormDB,
|
||||
redis: redisClient,
|
||||
}, cleanup, nil
|
||||
return d, cleanup, nil
|
||||
}
|
||||
|
||||
func (d *Data) switchDatabase(config *conf.Data_Database) error {
|
||||
db, err := openDatabase(config, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.mu.Lock()
|
||||
old := d.gormDB
|
||||
d.gormDB = db
|
||||
d.database = config
|
||||
d.config.Database = config
|
||||
d.mu.Unlock()
|
||||
if err := d.persistConfig(); err != nil {
|
||||
return fmt.Errorf("persist database configuration: %w", err)
|
||||
}
|
||||
if old != nil {
|
||||
if sqlDB, e := old.DB(); e == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
oracle "github.com/dzwvip/gorm-oracle"
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
var databaseNamePattern = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_$-]*$`)
|
||||
|
||||
func normalizedDriver(driver string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(driver)) {
|
||||
case "postgres", "postgresql", "pgsql":
|
||||
return "pgsql"
|
||||
case "sqlserver", "mssql":
|
||||
return "mssql"
|
||||
case "sqlite", "sqlite3":
|
||||
return "sqlite"
|
||||
case "oracle":
|
||||
return "oracle"
|
||||
default:
|
||||
return "mysql"
|
||||
}
|
||||
}
|
||||
|
||||
func databaseDSN(c *conf.Data_Database, name string) (string, error) {
|
||||
if c.Source != "" && c.Host == "" && c.Path == "" {
|
||||
return c.Source, nil
|
||||
}
|
||||
driver := normalizedDriver(c.Driver)
|
||||
if name == "" {
|
||||
name = c.Name
|
||||
}
|
||||
host := c.Host
|
||||
if host == "" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
switch driver {
|
||||
case "mysql":
|
||||
port := c.Port
|
||||
if port == "" {
|
||||
port = "3306"
|
||||
}
|
||||
query := c.Config
|
||||
if query == "" {
|
||||
query = "timeout=5s&parseTime=True&loc=Local&charset=utf8mb4"
|
||||
}
|
||||
return fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", c.User, c.Password, net.JoinHostPort(host, port), name, query), nil
|
||||
case "pgsql":
|
||||
port := c.Port
|
||||
if port == "" {
|
||||
port = "5432"
|
||||
}
|
||||
extra := c.Config
|
||||
if extra == "" {
|
||||
extra = "sslmode=disable TimeZone=Local"
|
||||
}
|
||||
return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s %s", host, port, c.User, c.Password, name, extra), nil
|
||||
case "mssql":
|
||||
port := c.Port
|
||||
if port == "" {
|
||||
port = "1433"
|
||||
}
|
||||
q := url.Values{"database": {name}, "encrypt": {"disable"}}
|
||||
return (&url.URL{Scheme: "sqlserver", User: url.UserPassword(c.User, c.Password), Host: net.JoinHostPort(host, port), RawQuery: q.Encode()}).String(), nil
|
||||
case "oracle":
|
||||
port := c.Port
|
||||
if port == "" {
|
||||
port = "1521"
|
||||
}
|
||||
return fmt.Sprintf("oracle://%s:%s@%s/%s?%s", url.PathEscape(c.User), url.PathEscape(c.Password), net.JoinHostPort(host, port), url.PathEscape(name), c.Config), nil
|
||||
case "sqlite":
|
||||
path := c.Path
|
||||
if path == "" {
|
||||
path = "."
|
||||
}
|
||||
if name == "" {
|
||||
name = "kra"
|
||||
}
|
||||
if filepath.Ext(name) == "" {
|
||||
name += ".db"
|
||||
}
|
||||
return filepath.Join(path, name), nil
|
||||
}
|
||||
return "", fmt.Errorf("unsupported database driver %q", c.Driver)
|
||||
}
|
||||
|
||||
func openWithDriver(driver, dsn string) (*gorm.DB, error) {
|
||||
switch normalizedDriver(driver) {
|
||||
case "mysql":
|
||||
return gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
case "pgsql":
|
||||
return gorm.Open(postgres.Open(dsn), &gorm.Config{})
|
||||
case "mssql":
|
||||
return gorm.Open(sqlserver.Open(dsn), &gorm.Config{})
|
||||
case "oracle":
|
||||
return gorm.Open(oracle.Open(dsn), &gorm.Config{})
|
||||
case "sqlite":
|
||||
return gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database driver %q", driver)
|
||||
}
|
||||
}
|
||||
|
||||
func openDatabase(c *conf.Data_Database, create bool) (*gorm.DB, error) {
|
||||
driver := normalizedDriver(c.Driver)
|
||||
if driver == "sqlite" {
|
||||
dsn, err := databaseDSN(c, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = os.MkdirAll(filepath.Dir(dsn), 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openWithDriver(driver, dsn)
|
||||
}
|
||||
if create && driver != "oracle" {
|
||||
if !databaseNamePattern.MatchString(c.Name) {
|
||||
return nil, fmt.Errorf("invalid database name %q", c.Name)
|
||||
}
|
||||
bootstrap := ""
|
||||
switch driver {
|
||||
case "pgsql":
|
||||
bootstrap = "postgres"
|
||||
case "mssql":
|
||||
bootstrap = "master"
|
||||
}
|
||||
dsn, err := databaseDSN(c, bootstrap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminDB, err := openWithDriver(driver, dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect database server: %w", err)
|
||||
}
|
||||
var statement string
|
||||
switch driver {
|
||||
case "mysql":
|
||||
statement = "CREATE DATABASE IF NOT EXISTS `" + c.Name + "` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"
|
||||
case "pgsql":
|
||||
var count int64
|
||||
if err = adminDB.Raw("SELECT count(*) FROM pg_database WHERE datname = ?", c.Name).Scan(&count).Error; err == nil && count == 0 {
|
||||
statement = `CREATE DATABASE "` + c.Name + `"`
|
||||
}
|
||||
case "mssql":
|
||||
statement = "IF DB_ID(N'" + c.Name + "') IS NULL CREATE DATABASE [" + c.Name + "]"
|
||||
}
|
||||
if statement != "" {
|
||||
err = adminDB.Exec(statement).Error
|
||||
}
|
||||
if sqlDB, e := adminDB.DB(); e == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create database: %w", err)
|
||||
}
|
||||
}
|
||||
dsn, err := databaseDSN(c, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return openWithDriver(driver, dsn)
|
||||
}
|
||||
|
||||
func openFallbackDatabase() (*gorm.DB, error) {
|
||||
return openWithDriver("sqlite", "file:kra-bootstrap?mode=memory&cache=shared")
|
||||
}
|
||||
|
|
@ -21,6 +21,33 @@ type fileStorage struct {
|
|||
}
|
||||
|
||||
func NewFileStorage(config *conf.AdminBackend) (biz.FileStorage, error) {
|
||||
storageType := "local"
|
||||
if config != nil && config.Storage != nil && config.Storage.Type != "" {
|
||||
storageType = strings.ToLower(config.Storage.Type)
|
||||
}
|
||||
if storageType == "qiniu" {
|
||||
return newQiniuStorage(config.Storage.Qiniu)
|
||||
}
|
||||
if storageType != "local" {
|
||||
var object *conf.AdminBackend_ObjectStore
|
||||
switch storageType {
|
||||
case "aliyun-oss":
|
||||
object = config.Storage.AliyunOss
|
||||
case "huawei-obs":
|
||||
object = config.Storage.HuaweiObs
|
||||
case "tencent-cos":
|
||||
object = config.Storage.TencentCos
|
||||
case "aws-s3":
|
||||
object = config.Storage.AwsS3
|
||||
case "cloudflare-r2":
|
||||
object = config.Storage.CloudflareR2
|
||||
case "minio":
|
||||
object = config.Storage.Minio
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported storage type %q", storageType)
|
||||
}
|
||||
return newS3Storage(storageType, object)
|
||||
}
|
||||
root, prefix := "uploads/file", "uploads/file"
|
||||
if config != nil && config.Local != nil {
|
||||
if config.Local.StorePath != "" {
|
||||
|
|
@ -37,6 +64,43 @@ func NewFileStorage(config *conf.AdminBackend) (biz.FileStorage, error) {
|
|||
return &fileStorage{root: root, urlPrefix: "/" + strings.Trim(prefix, "/")}, nil
|
||||
}
|
||||
|
||||
func composeFiles(ctx context.Context, storage biz.FileStorage, names []string, destination string) (*biz.StoredFile, string, error) {
|
||||
reader, writer := io.Pipe()
|
||||
hash := md5.New()
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer writer.Close()
|
||||
for _, name := range names {
|
||||
if ctx.Err() != nil {
|
||||
errCh <- ctx.Err()
|
||||
return
|
||||
}
|
||||
file, err := storage.Open(ctx, name)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(io.MultiWriter(writer, hash), file)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
errCh <- nil
|
||||
}()
|
||||
stored, err := storage.Put(ctx, destination, reader)
|
||||
composeErr := <-errCh
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if composeErr != nil {
|
||||
_ = storage.Delete(ctx, destination)
|
||||
return nil, "", composeErr
|
||||
}
|
||||
return stored, hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func (s *fileStorage) resolve(name string) (string, error) {
|
||||
clean := filepath.Clean(strings.TrimPrefix(name, "/"))
|
||||
if clean == "." || filepath.IsAbs(clean) || clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator)) {
|
||||
|
|
@ -88,40 +152,7 @@ func (s *fileStorage) Delete(ctx context.Context, name string) error {
|
|||
}
|
||||
|
||||
func (s *fileStorage) Compose(ctx context.Context, names []string, destination string) (*biz.StoredFile, string, error) {
|
||||
reader, writer := io.Pipe()
|
||||
hash := md5.New()
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer writer.Close()
|
||||
for _, name := range names {
|
||||
if ctx.Err() != nil {
|
||||
errCh <- ctx.Err()
|
||||
return
|
||||
}
|
||||
file, err := s.Open(ctx, name)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(io.MultiWriter(writer, hash), file)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
errCh <- nil
|
||||
}()
|
||||
stored, err := s.Put(ctx, destination, reader)
|
||||
composeErr := <-errCh
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if composeErr != nil {
|
||||
_ = s.Delete(ctx, destination)
|
||||
return nil, "", composeErr
|
||||
}
|
||||
return stored, hex.EncodeToString(hash.Sum(nil)), nil
|
||||
return composeFiles(ctx, s, names, destination)
|
||||
}
|
||||
func (s *fileStorage) DeletePrefix(ctx context.Context, prefix string) error {
|
||||
path, err := s.resolve(strings.TrimSuffix(prefix, "/") + "/placeholder")
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func (r *mediaRepo) DeleteMedia(ctx context.Context, id uint) error {
|
|||
}
|
||||
func (r *mediaRepo) MediaKeyReferences(ctx context.Context, key string) (int64, error) {
|
||||
var count int64
|
||||
err := r.data.gormDB.WithContext(ctx).Model(&mediaPO{}).Where("`key` = ?", key).Count(&count).Error
|
||||
err := r.data.gormDB.WithContext(ctx).Model(&mediaPO{}).Where(map[string]any{"key": key}).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
func (r *mediaRepo) CreateMediaBatch(ctx context.Context, items []*biz.MediaFile) error {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
qstorage "github.com/qiniu/go-sdk/v7/storage"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
type qiniuStorage struct {
|
||||
config *conf.AdminBackend_Qiniu
|
||||
upload *qstorage.FormUploader
|
||||
manager *qstorage.BucketManager
|
||||
mac *qbox.Mac
|
||||
token string
|
||||
}
|
||||
|
||||
func newQiniuStorage(config *conf.AdminBackend_Qiniu) (biz.FileStorage, error) {
|
||||
if config == nil || config.Bucket == "" || config.AccessKey == "" || config.SecretKey == "" {
|
||||
return nil, fmt.Errorf("qiniu storage configuration is incomplete")
|
||||
}
|
||||
cfg := qstorage.Config{UseHTTPS: config.UseHttps, UseCdnDomains: config.UseCdnDomains}
|
||||
switch config.Zone {
|
||||
case "ZoneHuadong":
|
||||
cfg.Zone = &qstorage.ZoneHuadong
|
||||
case "ZoneHuabei":
|
||||
cfg.Zone = &qstorage.ZoneHuabei
|
||||
case "ZoneHuanan":
|
||||
cfg.Zone = &qstorage.ZoneHuanan
|
||||
case "ZoneBeimei":
|
||||
cfg.Zone = &qstorage.ZoneBeimei
|
||||
case "ZoneXinjiapo":
|
||||
cfg.Zone = &qstorage.ZoneXinjiapo
|
||||
}
|
||||
mac := qbox.NewMac(config.AccessKey, config.SecretKey)
|
||||
policy := qstorage.PutPolicy{Scope: config.Bucket}
|
||||
token := policy.UploadToken(mac)
|
||||
return &qiniuStorage{config: config, upload: qstorage.NewFormUploader(&cfg), manager: qstorage.NewBucketManager(mac, &cfg), mac: mac, token: token}, nil
|
||||
}
|
||||
func (s *qiniuStorage) file(key string, size int64) *biz.StoredFile {
|
||||
return &biz.StoredFile{Name: path.Base(key), Path: key, URL: strings.TrimSuffix(s.config.BaseUrl, "/") + "/" + key, Size: size}
|
||||
}
|
||||
func (s *qiniuStorage) Put(ctx context.Context, name string, reader io.Reader) (*biz.StoredFile, error) {
|
||||
temporary, err := os.CreateTemp("", "kra-qiniu-upload-*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
temporaryName := temporary.Name()
|
||||
defer os.Remove(temporaryName)
|
||||
size, err := io.Copy(temporary, reader)
|
||||
if err != nil {
|
||||
_ = temporary.Close()
|
||||
return nil, err
|
||||
}
|
||||
if _, err = temporary.Seek(0, io.SeekStart); err != nil {
|
||||
_ = temporary.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer temporary.Close()
|
||||
ret := qstorage.PutRet{}
|
||||
if err = s.upload.Put(ctx, &ret, s.token, strings.TrimPrefix(name, "/"), temporary, size, &qstorage.PutExtra{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.file(ret.Key, size), nil
|
||||
}
|
||||
func (s *qiniuStorage) Open(ctx context.Context, name string) (io.ReadCloser, error) {
|
||||
downloadURL := qstorage.MakePrivateURL(s.mac, strings.TrimSuffix(s.config.BaseUrl, "/"), strings.TrimPrefix(name, "/"), time.Now().Add(time.Hour).Unix())
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
_ = resp.Body.Close()
|
||||
return nil, fmt.Errorf("qiniu download failed: %s", resp.Status)
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
func (s *qiniuStorage) Delete(ctx context.Context, name string) error {
|
||||
return s.manager.Delete(s.config.Bucket, strings.TrimPrefix(name, "/"))
|
||||
}
|
||||
func (s *qiniuStorage) Compose(ctx context.Context, names []string, destination string) (*biz.StoredFile, string, error) {
|
||||
return composeFiles(ctx, s, names, destination)
|
||||
}
|
||||
func (s *qiniuStorage) DeletePrefix(ctx context.Context, prefix string) error {
|
||||
for {
|
||||
items, _, more, err := s.List(ctx, prefix, "", 1000)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range items {
|
||||
if err = s.Delete(ctx, item.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !more {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *qiniuStorage) List(ctx context.Context, prefix, cursor string, limit int) ([]*biz.StoredFile, string, bool, error) {
|
||||
if limit < 1 || limit > 1000 {
|
||||
limit = 100
|
||||
}
|
||||
entries, _, marker, more, err := s.manager.ListFiles(s.config.Bucket, prefix, "", cursor, limit)
|
||||
if err != nil {
|
||||
return nil, "", false, err
|
||||
}
|
||||
out := make([]*biz.StoredFile, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
out = append(out, s.file(entry.Key, entry.Fsize))
|
||||
}
|
||||
return out, marker, more, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
)
|
||||
|
||||
type s3Storage struct {
|
||||
client *minio.Client
|
||||
bucket, baseURL, prefix string
|
||||
}
|
||||
|
||||
func newS3Storage(provider string, config *conf.AdminBackend_ObjectStore) (biz.FileStorage, error) {
|
||||
if config == nil || config.Bucket == "" || config.AccessKey == "" || config.SecretKey == "" {
|
||||
return nil, fmt.Errorf("%s storage configuration is incomplete", provider)
|
||||
}
|
||||
endpoint := config.Endpoint
|
||||
if endpoint == "" {
|
||||
switch provider {
|
||||
case "aws-s3":
|
||||
endpoint = "s3." + config.Region + ".amazonaws.com"
|
||||
case "cloudflare-r2":
|
||||
endpoint = config.AccountId + ".r2.cloudflarestorage.com"
|
||||
case "aliyun-oss":
|
||||
endpoint = "oss-" + config.Region + ".aliyuncs.com"
|
||||
case "huawei-obs":
|
||||
endpoint = "obs." + config.Region + ".myhuaweicloud.com"
|
||||
case "tencent-cos":
|
||||
endpoint = "cos." + config.Region + ".myqcloud.com"
|
||||
}
|
||||
}
|
||||
secure := config.UseSsl
|
||||
if parsed, err := url.Parse(endpoint); err == nil && parsed.Host != "" {
|
||||
secure = parsed.Scheme == "https"
|
||||
endpoint = parsed.Host
|
||||
}
|
||||
if endpoint == "" {
|
||||
return nil, fmt.Errorf("%s endpoint is required", provider)
|
||||
}
|
||||
client, err := minio.New(strings.TrimSuffix(endpoint, "/"), &minio.Options{Creds: credentials.NewStaticV4(config.AccessKey, config.SecretKey, ""), Secure: secure, Region: config.Region, BucketLookup: func() minio.BucketLookupType {
|
||||
if config.ForcePathStyle {
|
||||
return minio.BucketLookupPath
|
||||
}
|
||||
return minio.BucketLookupAuto
|
||||
}()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s3Storage{client: client, bucket: config.Bucket, baseURL: strings.TrimSuffix(config.BaseUrl, "/"), prefix: strings.Trim(config.PathPrefix, "/")}, nil
|
||||
}
|
||||
func (s *s3Storage) key(name string) string {
|
||||
if s.prefix == "" {
|
||||
return strings.TrimPrefix(name, "/")
|
||||
}
|
||||
return path.Join(s.prefix, strings.TrimPrefix(name, "/"))
|
||||
}
|
||||
func (s *s3Storage) unkey(key string) string {
|
||||
return strings.TrimPrefix(strings.TrimPrefix(key, s.prefix), "/")
|
||||
}
|
||||
func (s *s3Storage) file(key string, size int64) *biz.StoredFile {
|
||||
name := s.unkey(key)
|
||||
rawURL := s.baseURL + "/" + key
|
||||
if s.baseURL == "" {
|
||||
rawURL = key
|
||||
}
|
||||
return &biz.StoredFile{Name: path.Base(name), Path: name, URL: rawURL, Size: size}
|
||||
}
|
||||
func (s *s3Storage) Put(ctx context.Context, name string, reader io.Reader) (*biz.StoredFile, error) {
|
||||
key := s.key(name)
|
||||
info, err := s.client.PutObject(ctx, s.bucket, key, reader, -1, minio.PutObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.file(key, info.Size), nil
|
||||
}
|
||||
func (s *s3Storage) Open(ctx context.Context, name string) (io.ReadCloser, error) {
|
||||
obj, err := s.client.GetObject(ctx, s.bucket, s.key(name), minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = obj.Stat(); err != nil {
|
||||
_ = obj.Close()
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
func (s *s3Storage) Delete(ctx context.Context, name string) error {
|
||||
return s.client.RemoveObject(ctx, s.bucket, s.key(name), minio.RemoveObjectOptions{})
|
||||
}
|
||||
func (s *s3Storage) Compose(ctx context.Context, names []string, destination string) (*biz.StoredFile, string, error) {
|
||||
return composeFiles(ctx, s, names, destination)
|
||||
}
|
||||
func (s *s3Storage) DeletePrefix(ctx context.Context, prefix string) error {
|
||||
items := s.client.ListObjects(ctx, s.bucket, minio.ListObjectsOptions{Prefix: s.key(prefix), Recursive: true})
|
||||
for item := range items {
|
||||
if item.Err != nil {
|
||||
return item.Err
|
||||
}
|
||||
if err := s.client.RemoveObject(ctx, s.bucket, item.Key, minio.RemoveObjectOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *s3Storage) List(ctx context.Context, prefix, cursor string, limit int) ([]*biz.StoredFile, string, bool, error) {
|
||||
if limit < 1 || limit > 1000 {
|
||||
limit = 100
|
||||
}
|
||||
items := s.client.ListObjects(ctx, s.bucket, minio.ListObjectsOptions{Prefix: s.key(prefix), Recursive: true, StartAfter: s.key(cursor)})
|
||||
out := make([]*biz.StoredFile, 0, limit+1)
|
||||
for item := range items {
|
||||
if item.Err != nil {
|
||||
return nil, "", false, item.Err
|
||||
}
|
||||
out = append(out, s.file(item.Key, item.Size))
|
||||
if len(out) > limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
more := len(out) > limit
|
||||
if more {
|
||||
out = out[:limit]
|
||||
}
|
||||
next := ""
|
||||
if len(out) > 0 {
|
||||
next = out[len(out)-1].Path
|
||||
}
|
||||
return out, next, more, nil
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"kra/internal/biz"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type dictionaryPO struct {
|
||||
|
|
@ -334,7 +335,7 @@ func (r *settingsRepo) FindParameter(ctx context.Context, id uint, key string) (
|
|||
if id != 0 {
|
||||
err = db.First(&po, id).Error
|
||||
} else {
|
||||
err = db.Where("`key` = ?", key).First(&po).Error
|
||||
err = db.Where(map[string]any{"key": key}).First(&po).Error
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -354,7 +355,7 @@ func (r *settingsRepo) ListParameters(ctx context.Context, page, size int, q *bi
|
|||
db = db.Where("name LIKE ?", "%"+q.Name+"%")
|
||||
}
|
||||
if q.Key != "" {
|
||||
db = db.Where("`key` LIKE ?", "%"+q.Key+"%")
|
||||
db = db.Where(clause.Like{Column: clause.Column{Name: "key"}, Value: "%" + q.Key + "%"})
|
||||
}
|
||||
}
|
||||
var total int64
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
|
@ -87,14 +88,22 @@ func (authorityMenuPO) TableName() string { return "sys_authority_menus" }
|
|||
|
||||
type systemRepo struct{ data *Data }
|
||||
|
||||
func NewSystemRepo(data *Data) biz.SystemRepo { return &systemRepo{data: data} }
|
||||
func NewSystemRepo(data *Data) biz.SystemRepo { return &systemRepo{data: data} }
|
||||
func (r *systemRepo) PersistConfig(context.Context) error { return r.data.persistConfig() }
|
||||
|
||||
func (r *systemRepo) IsInitialized(ctx context.Context) (bool, error) {
|
||||
return r.data.gormDB.WithContext(ctx).Migrator().HasTable(&userPO{}), nil
|
||||
}
|
||||
|
||||
func (r *systemRepo) Initialize(ctx context.Context) error {
|
||||
func (r *systemRepo) Initialize(ctx context.Context, input *biz.DatabaseConfig) error {
|
||||
config := &conf.Data_Database{Driver: input.Driver, Host: input.Host, Port: input.Port, User: input.User, Password: input.Password, Name: input.Name, Path: input.Path, Config: input.Config}
|
||||
if err := r.data.switchDatabase(config); err != nil {
|
||||
return err
|
||||
}
|
||||
db := r.data.gormDB.WithContext(ctx)
|
||||
if err := db.AutoMigrate(&adminPO{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.AutoMigrate(&userPO{}, &authorityPO{}, &menuPO{}, &userAuthorityPO{}, &authorityMenuPO{}, &apiPO{}, &ignoredAPIPO{}, &authorityAPIPO{}, &menuButtonPO{}, &authorityButtonPO{}, &departmentPO{}, &positionPO{}, &userDepartmentPO{}, &userPositionPO{}, &authorityDepartmentPO{}, &dictionaryPO{}, &dictionaryDetailPO{}, ¶meterPO{}, &apiTokenPO{}, &securityConfigPO{}, &versionPO{}, &exportTemplatePO{}, &exportConditionPO{}, &exportJoinPO{}, &operationPO{}, &loginLogPO{}, &dataAccessLogPO{}, &errorRecordPO{}, &taskPO{}, &taskLogPO{}, &mediaPO{}, &categoryPO{}, &uploadSessionPO{}, &uploadChunkPO{}, &announcementPO{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -140,7 +149,7 @@ func (r *systemRepo) Initialize(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(input.AdminPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -349,9 +358,13 @@ func (r *systemRepo) DeleteUser(ctx context.Context, id uint) error {
|
|||
return tx.Delete(&userPO{}, id).Error
|
||||
})
|
||||
}
|
||||
func (r *systemRepo) UpdatePassword(ctx context.Context, id uint, password string) error {
|
||||
func (r *systemRepo) UpdatePassword(ctx context.Context, id uint, password string, clearMustChange bool) error {
|
||||
now := time.Now()
|
||||
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Updates(map[string]any{"password": password, "password_updated_at": now, "must_change_password": false}).Error
|
||||
updates := map[string]any{"password": password, "password_updated_at": now}
|
||||
if clearMustChange {
|
||||
updates["must_change_password"] = false
|
||||
}
|
||||
return r.data.gormDB.WithContext(ctx).Model(&userPO{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
func (r *systemRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) {
|
||||
var pos []authorityPO
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func redactJSON(raw []byte) string {
|
|||
}
|
||||
func operationAudit(svc *service.AuditService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if c.Request.Method == "GET" || c.Request.Method == "HEAD" || c.Request.Method == "OPTIONS" {
|
||||
if isBootstrapPath(c.Request.URL.Path) || c.Request.Method == "GET" || c.Request.Method == "HEAD" || c.Request.Method == "OPTIONS" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
|||
gin.SetMode(gin.ReleaseMode)
|
||||
engine := gin.New()
|
||||
engine.Use(ginRequestMeta(), emailErrorAlert(emails, logger), gin.Recovery(), securityRateLimit(svc, settings), operationAudit(audit))
|
||||
if config != nil && config.Local != nil && config.Local.StorePath != "" {
|
||||
localStorage := config == nil || config.Storage == nil || config.Storage.Type == "" || config.Storage.Type == "local"
|
||||
if localStorage && config != nil && config.Local != nil && config.Local.StorePath != "" {
|
||||
pathPrefix := "/" + strings.Trim(config.Local.PathPrefix, "/")
|
||||
if pathPrefix != "/" {
|
||||
engine.StaticFS(pathPrefix, filesOnly{FileSystem: http.Dir(config.Local.StorePath)})
|
||||
|
|
@ -51,11 +52,11 @@ func NewGinServer(c *conf.Server, config *conf.AdminBackend, svc *service.System
|
|||
})
|
||||
registerPublicRoutes(public, engine, config, svc, settings, access, audit)
|
||||
private := engine.Group(prefix)
|
||||
private.Use(jwtAuth(config, settings), accessControl(access, audit))
|
||||
private.Use(jwtAuth(config, settings), mustChangePasswordGuard(), accessControl(access, audit))
|
||||
registerPrivateRoutes(private, svc)
|
||||
registerAccessRoutes(private, engine, access)
|
||||
registerSettingsRoutes(private, public, settings)
|
||||
registerSystemConfigRoutes(private, config, settings)
|
||||
registerSystemConfigRoutes(private, config, svc, settings)
|
||||
registerVersionRoutes(private, versions)
|
||||
registerExportRoutes(private, public, svc, exports)
|
||||
registerAuditRoutes(private, public, audit)
|
||||
|
|
@ -117,6 +118,22 @@ func currentClaims(c *gin.Context) *adminauth.Claims {
|
|||
return result
|
||||
}
|
||||
|
||||
func mustChangePasswordGuard() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
claims := currentClaims(c)
|
||||
if claims == nil || !claims.MustChangePwd {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
path := strings.TrimSuffix(c.Request.URL.Path, "/")
|
||||
if strings.HasSuffix(path, "/user/changePassword") || strings.HasSuffix(path, "/user/getUserInfo") || strings.HasSuffix(path, "/jwt/jsonInBlacklist") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
c.AbortWithStatusJSON(http.StatusConflict, response{Code: codePasswordChangeRequired, Data: gin.H{"needChangePassword": true}, Msg: "请先修改初始密码"})
|
||||
}
|
||||
}
|
||||
|
||||
func registerPublicRoutes(group *gin.RouterGroup, engine *gin.Engine, config *conf.AdminBackend, svc *service.SystemService, settings *service.SettingsService, access *service.AccessService, audit *service.AuditService) {
|
||||
expiration := 3 * time.Minute
|
||||
keyLong, width, height := 6, 240, 80
|
||||
|
|
@ -221,8 +238,13 @@ func registerPublicRoutes(group *gin.RouterGroup, engine *gin.Engine, config *co
|
|||
writeResult(c, codeSuccess, gin.H{"needInit": !initialized}, message)
|
||||
})
|
||||
initGroup.POST("/initdb", func(c *gin.Context) {
|
||||
if err := svc.Initialize(c.Request.Context()); err != nil {
|
||||
fail(c, "自动创建数据库失败")
|
||||
var input service.DatabaseInit
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
fail(c, "数据库初始化参数无效")
|
||||
return
|
||||
}
|
||||
if err := svc.Initialize(c.Request.Context(), &input); err != nil {
|
||||
fail(c, "自动创建数据库失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
routes := engine.Routes()
|
||||
|
|
@ -261,6 +283,10 @@ func incrementCached(ctx context.Context, svc *service.SystemService, key string
|
|||
}
|
||||
func securityRateLimit(svc *service.SystemService, settings *service.SettingsService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if isBootstrapPath(c.Request.URL.Path) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
cfg, err := settings.CurrentSecurity(c.Request.Context())
|
||||
if err != nil || cfg == nil || !cfg.LimitEnable {
|
||||
c.Next()
|
||||
|
|
@ -281,6 +307,10 @@ func securityRateLimit(svc *service.SystemService, settings *service.SettingsSer
|
|||
}
|
||||
}
|
||||
|
||||
func isBootstrapPath(path string) bool {
|
||||
return strings.HasSuffix(path, "/health") || strings.Contains(path, "/init/")
|
||||
}
|
||||
|
||||
type captchaStore struct {
|
||||
service *service.SystemService
|
||||
expiration time.Duration
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@ package server
|
|||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBackend, settings *service.SettingsService) {
|
||||
func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBackend, systemService *service.SystemService, settings *service.SettingsService) {
|
||||
security := group.Group("/securityConfig")
|
||||
security.GET("/getSecurityConfig", func(c *gin.Context) {
|
||||
value, err := settings.Security(c.Request.Context())
|
||||
|
|
@ -53,6 +57,11 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
|||
if config.Email != nil {
|
||||
email = gin.H{"to": config.Email.To, "from": config.Email.From, "host": config.Email.Host, "secret": "******", "nickname": config.Email.Nickname, "port": config.Email.Port, "is-ssl": config.Email.IsSsl, "is-loginauth": config.Email.IsLoginAuth}
|
||||
}
|
||||
if config.Storage != nil {
|
||||
storage := proto.Clone(config.Storage).(*conf.AdminBackend_Storage)
|
||||
maskStorageSecrets(storage)
|
||||
admin["storage"] = storage
|
||||
}
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{"config": gin.H{"admin": admin, "email": email}}, "获取成功")
|
||||
})
|
||||
|
|
@ -69,6 +78,7 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
|||
StorePath string `json:"storePath"`
|
||||
PathPrefix string `json:"pathPrefix"`
|
||||
} `json:"local"`
|
||||
Storage *conf.AdminBackend_Storage `json:"storage"`
|
||||
} `json:"admin"`
|
||||
Email *struct {
|
||||
To string `json:"to"`
|
||||
|
|
@ -104,6 +114,10 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
|||
config.Local.PathPrefix = req.Config.Admin.Local.PathPrefix
|
||||
}
|
||||
}
|
||||
if req.Config.Admin.Storage != nil {
|
||||
preserveStorageSecrets(req.Config.Admin.Storage, config.Storage)
|
||||
config.Storage = req.Config.Admin.Storage
|
||||
}
|
||||
if config.Email != nil && req.Config.Email != nil {
|
||||
config.Email.To = req.Config.Email.To
|
||||
config.Email.From = req.Config.Email.From
|
||||
|
|
@ -117,12 +131,18 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
|||
}
|
||||
}
|
||||
}
|
||||
ok(c)
|
||||
if err := systemService.PersistConfig(c.Request.Context()); err != nil {
|
||||
fail(c, "配置保存失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
writeResult(c, codeSuccess, gin.H{"restartRequired": true}, "配置已保存,重启服务后完整生效")
|
||||
})
|
||||
system.POST("/reloadSystem", func(c *gin.Context) {
|
||||
writeResult(c, codeSuccess, gin.H{"reload": false, "restartRequired": true}, "配置已在当前进程部分生效;路由与存储配置需重启服务")
|
||||
})
|
||||
system.POST("/reloadSystem", func(c *gin.Context) { writeResult(c, codeSuccess, gin.H{"reload": true}, "配置已热更新") })
|
||||
system.POST("/getServerInfo", func(c *gin.Context) {
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
cpuPercent, _ := cpu.Percent(200*time.Millisecond, false)
|
||||
memory, _ := mem.VirtualMemory()
|
||||
disk := []gin.H{}
|
||||
var stat syscall.Statfs_t
|
||||
if syscall.Statfs(".", &stat) == nil {
|
||||
|
|
@ -135,7 +155,42 @@ func registerSystemConfigRoutes(group *gin.RouterGroup, config *conf.AdminBacken
|
|||
}
|
||||
disk = append(disk, gin.H{"mountPoint": ".", "usedMb": used / 1024 / 1024, "usedGb": used / 1024 / 1024 / 1024, "totalMb": total / 1024 / 1024, "totalGb": total / 1024 / 1024 / 1024, "usedPercent": percent})
|
||||
}
|
||||
server := gin.H{"os": gin.H{"goos": runtime.GOOS, "numCpu": runtime.NumCPU(), "compiler": runtime.Compiler, "goVersion": runtime.Version(), "numGoroutine": runtime.NumGoroutine()}, "cpu": gin.H{"cpus": []float64{}, "cores": runtime.NumCPU()}, "ram": gin.H{"usedMb": mem.Alloc / 1024 / 1024, "totalMb": mem.Sys / 1024 / 1024, "usedPercent": 0}, "disk": disk}
|
||||
usedMB, totalMB, memoryPercent := uint64(0), uint64(0), float64(0)
|
||||
if memory != nil {
|
||||
usedMB, totalMB, memoryPercent = memory.Used/1024/1024, memory.Total/1024/1024, memory.UsedPercent
|
||||
}
|
||||
server := gin.H{"os": gin.H{"goos": runtime.GOOS, "numCpu": runtime.NumCPU(), "compiler": runtime.Compiler, "goVersion": runtime.Version(), "numGoroutine": runtime.NumGoroutine()}, "cpu": gin.H{"cpus": cpuPercent, "cores": runtime.NumCPU()}, "ram": gin.H{"usedMb": usedMB, "totalMb": totalMB, "usedPercent": memoryPercent}, "disk": disk}
|
||||
writeResult(c, codeSuccess, gin.H{"server": server}, "获取成功")
|
||||
})
|
||||
}
|
||||
|
||||
func objectStores(storage *conf.AdminBackend_Storage) []*conf.AdminBackend_ObjectStore {
|
||||
if storage == nil {
|
||||
return nil
|
||||
}
|
||||
return []*conf.AdminBackend_ObjectStore{storage.AliyunOss, storage.HuaweiObs, storage.TencentCos, storage.AwsS3, storage.CloudflareR2, storage.Minio}
|
||||
}
|
||||
func maskStorageSecrets(storage *conf.AdminBackend_Storage) {
|
||||
if storage.Qiniu != nil {
|
||||
storage.Qiniu.SecretKey = "******"
|
||||
}
|
||||
for _, item := range objectStores(storage) {
|
||||
if item != nil {
|
||||
item.SecretKey = "******"
|
||||
}
|
||||
}
|
||||
}
|
||||
func preserveStorageSecrets(next, current *conf.AdminBackend_Storage) {
|
||||
if next == nil || current == nil {
|
||||
return
|
||||
}
|
||||
if next.Qiniu != nil && current.Qiniu != nil && (next.Qiniu.SecretKey == "" || next.Qiniu.SecretKey == "******") {
|
||||
next.Qiniu.SecretKey = current.Qiniu.SecretKey
|
||||
}
|
||||
nextItems, currentItems := objectStores(next), objectStores(current)
|
||||
for i := range nextItems {
|
||||
if nextItems[i] != nil && currentItems[i] != nil && (nextItems[i].SecretKey == "" || nextItems[i].SecretKey == "******") {
|
||||
nextItems[i].SecretKey = currentItems[i].SecretKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,30 @@ func NewSystemService(uc *biz.SystemUsecase, config *conf.AdminBackend, settings
|
|||
func (s *SystemService) IsInitialized(ctx context.Context) (bool, error) {
|
||||
return s.uc.IsInitialized(ctx)
|
||||
}
|
||||
func (s *SystemService) Initialize(ctx context.Context) error { return s.uc.Initialize(ctx) }
|
||||
func (s *SystemService) PersistConfig(ctx context.Context) error { return s.uc.PersistConfig(ctx) }
|
||||
|
||||
type DatabaseInit struct {
|
||||
DBType string `json:"dbType"`
|
||||
Host string `json:"host"`
|
||||
Port string `json:"port"`
|
||||
UserName string `json:"userName"`
|
||||
Password string `json:"password"`
|
||||
DBName string `json:"dbName"`
|
||||
DBPath string `json:"dbPath"`
|
||||
Template string `json:"template"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
}
|
||||
|
||||
func (s *SystemService) Initialize(ctx context.Context, input *DatabaseInit) error {
|
||||
config := ""
|
||||
switch input.DBType {
|
||||
case "mysql":
|
||||
config = "timeout=5s&parseTime=True&loc=Local&charset=utf8mb4"
|
||||
case "pgsql":
|
||||
config = "sslmode=disable TimeZone=Local"
|
||||
}
|
||||
return s.uc.Initialize(ctx, &biz.DatabaseConfig{Driver: input.DBType, Host: input.Host, Port: input.Port, User: input.UserName, Password: input.Password, Name: input.DBName, Path: input.DBPath, Config: config, AdminPassword: input.AdminPassword})
|
||||
}
|
||||
|
||||
func (s *SystemService) CacheGet(ctx context.Context, key string) (string, bool, error) {
|
||||
return s.uc.CacheGet(ctx, key)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
"axios": "1.8.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"echarts": "5.5.1",
|
||||
"element-plus": "^2.13.6",
|
||||
"highlight.js": "^11.10.0",
|
||||
"mitt": "^3.0.1",
|
||||
|
|
@ -43,7 +42,6 @@
|
|||
"vite-auto-import-svg": "^2.9.8",
|
||||
"vue": "^3.5.31",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-qr": "^4.0.9",
|
||||
"vue-router": "^4.4.3",
|
||||
"vuedraggable": "^4.1.0"
|
||||
|
|
|
|||
|
|
@ -53,9 +53,6 @@ importers:
|
|||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
echarts:
|
||||
specifier: 5.5.1
|
||||
version: 5.5.1
|
||||
element-plus:
|
||||
specifier: ^2.13.6
|
||||
version: 2.14.4(vue@3.5.41)
|
||||
|
|
@ -101,9 +98,6 @@ importers:
|
|||
vue-cropper:
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4
|
||||
vue-echarts:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3(@vue/runtime-core@3.5.41)(echarts@5.5.1)(vue@3.5.41)
|
||||
vue-qr:
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
|
|
@ -1604,9 +1598,6 @@ packages:
|
|||
duplexer@0.1.2:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
|
||||
echarts@5.5.1:
|
||||
resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==}
|
||||
|
||||
electron-to-chromium@1.5.405:
|
||||
resolution: {integrity: sha512-bNglH7lPH5l+yHOes7Zr4VqxhOy4BQ9ZBUX4VdoFgxMpzJk7W1ZoO3Vgd9Pxa9PyjQ76sfm2aKH/nzEcCNRlew==}
|
||||
|
||||
|
|
@ -2473,9 +2464,6 @@ packages:
|
|||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tslib@2.3.0:
|
||||
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
|
|
@ -2621,17 +2609,6 @@ packages:
|
|||
vue-cropper@1.1.4:
|
||||
resolution: {integrity: sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==}
|
||||
|
||||
vue-demi@0.13.11:
|
||||
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-demi@0.14.10:
|
||||
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||
engines: {node: '>=12'}
|
||||
|
|
@ -2643,16 +2620,6 @@ packages:
|
|||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue-echarts@7.0.3:
|
||||
resolution: {integrity: sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==}
|
||||
peerDependencies:
|
||||
'@vue/runtime-core': ^3.0.0
|
||||
echarts: ^5.5.1
|
||||
vue: ^2.7.0 || ^3.1.1
|
||||
peerDependenciesMeta:
|
||||
'@vue/runtime-core':
|
||||
optional: true
|
||||
|
||||
vue-eslint-parser@9.4.3:
|
||||
resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==}
|
||||
engines: {node: ^14.17.0 || >=16.0.0}
|
||||
|
|
@ -2710,9 +2677,6 @@ packages:
|
|||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zrender@5.6.0:
|
||||
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/code-frame@7.29.7':
|
||||
|
|
@ -4295,11 +4259,6 @@ snapshots:
|
|||
|
||||
duplexer@0.1.2: {}
|
||||
|
||||
echarts@5.5.1:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
zrender: 5.6.0
|
||||
|
||||
electron-to-chromium@1.5.405: {}
|
||||
|
||||
element-plus@2.14.4(vue@3.5.41):
|
||||
|
|
@ -5125,8 +5084,6 @@ snapshots:
|
|||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tslib@2.3.0: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
|
|
@ -5258,24 +5215,10 @@ snapshots:
|
|||
|
||||
vue-cropper@1.1.4: {}
|
||||
|
||||
vue-demi@0.13.11(vue@3.5.41):
|
||||
dependencies:
|
||||
vue: 3.5.41
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.41):
|
||||
dependencies:
|
||||
vue: 3.5.41
|
||||
|
||||
vue-echarts@7.0.3(@vue/runtime-core@3.5.41)(echarts@5.5.1)(vue@3.5.41):
|
||||
dependencies:
|
||||
echarts: 5.5.1
|
||||
vue: 3.5.41
|
||||
vue-demi: 0.13.11(vue@3.5.41)
|
||||
optionalDependencies:
|
||||
'@vue/runtime-core': 3.5.41
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
vue-eslint-parser@9.4.3(eslint@8.57.1(supports-color@7.2.0))(supports-color@7.2.0):
|
||||
dependencies:
|
||||
debug: 4.4.3(supports-color@7.2.0)
|
||||
|
|
@ -5334,7 +5277,3 @@ snapshots:
|
|||
yallist@3.1.1: {}
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zrender@5.6.0:
|
||||
dependencies:
|
||||
tslib: 2.3.0
|
||||
|
|
|
|||
|
|
@ -76,14 +76,6 @@ export const updateApi = (data) => {
|
|||
// @Param data body api.CreateApiParams true "更新api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /api/setAuthApi [post]
|
||||
export const setAuthApi = (data) => {
|
||||
return service({
|
||||
url: '/api/setAuthApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
|
|
|
|||
|
|
@ -102,9 +102,3 @@ export const getSysErrorList = (params) => {
|
|||
// @Param data query systemReq.SysErrorSearch true "分页获取错误日志列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysError/getSysErrorPublic [get]
|
||||
export const getSysErrorPublic = () => {
|
||||
return service({
|
||||
url: '/sysError/getSysErrorPublic',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
<template>
|
||||
<VCharts
|
||||
v-if="renderChart"
|
||||
:option="options"
|
||||
:autoresize="autoResize"
|
||||
:style="{ width, height }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, nextTick } from 'vue'
|
||||
import VCharts from 'vue-echarts'
|
||||
import { useWindowResize } from '@/hooks/use-windows-resize'
|
||||
|
||||
defineProps({
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
autoResize: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
})
|
||||
const renderChart = ref(false)
|
||||
nextTick(() => {
|
||||
renderChart.value = true
|
||||
})
|
||||
useWindowResize(() => {
|
||||
renderChart.value = false
|
||||
nextTick(() => {
|
||||
renderChart.value = true
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// 基础组件库:以无样式的 reka-ui 原语为底座,仅用项目既有的 UnoCSS 主题 token 上色,
|
||||
// 跟随 themeStore 换肤 / 暗色模式。规范见 aiDoc/frontend-backend/component-library.md。
|
||||
// 跟随 themeStore 换肤与暗色模式。
|
||||
export * from './button'
|
||||
export * from './input'
|
||||
export * from './checkbox'
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import * as ElIconModules from '@element-plus/icons-vue'
|
|||
import svgIcon from '@/components/svgIcon/svgIcon.vue'
|
||||
// 基础组件库(reka-ui 底座,src/core/componentLibrary):全局以 g- 前缀(kebab-case)注册,
|
||||
// 全站可直接 <g-button /> / <g-select /> 使用,无需逐文件 import。
|
||||
// 规范见 aiDoc/frontend-backend/component-library.md
|
||||
import * as ComponentLibrary from '@/core/componentLibrary'
|
||||
// 导入转换图标名称的函数
|
||||
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
<template>
|
||||
<Chart :height="height" :option="chartOption" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Chart from '@/components/charts/index.vue'
|
||||
import useChartOption from '@/hooks/charts'
|
||||
import { graphic } from 'echarts'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useThemeStore } from '@/pinia'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { addOpacityToColor } from '@/theme/color'
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
defineProps({
|
||||
height: {
|
||||
type: String,
|
||||
default: '128px'
|
||||
}
|
||||
})
|
||||
const axisTextColor = computed(() => {
|
||||
return themeStore.isDark ? 'rgba(255,255,255,0.70)' : 'rgba(0,0,0,0.70)'
|
||||
})
|
||||
const dotColor = computed(() => {
|
||||
return themeStore.isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)'
|
||||
})
|
||||
const primaryColor = (opacity) => addOpacityToColor(settings.value.themeColor, opacity)
|
||||
const graphicFactory = (side) => {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: axisTextColor.value,
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
const xAxis = ref([
|
||||
'2024-1',
|
||||
'2024-2',
|
||||
'2024-3',
|
||||
'2024-4',
|
||||
'2024-5',
|
||||
'2024-6',
|
||||
'2024-7',
|
||||
'2024-8'
|
||||
])
|
||||
const chartsData = ref([12, 22, 32, 45, 32, 78, 89, 92])
|
||||
const graphicElements = ref([
|
||||
graphicFactory({ left: '5%' }),
|
||||
graphicFactory({ right: 0 })
|
||||
])
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: '40',
|
||||
right: '0',
|
||||
top: '10',
|
||||
bottom: '30'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 2,
|
||||
data: xAxis.value,
|
||||
boundaryGap: false,
|
||||
axisLabel: {
|
||||
color: axisTextColor.value,
|
||||
formatter(value, idx) {
|
||||
if (idx === 0) return ''
|
||||
if (idx === xAxis.value.length - 1) return ''
|
||||
return `${value}`
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
interval: (idx) => {
|
||||
if (idx === 0) return false
|
||||
if (idx === xAxis.value.length - 1) return false
|
||||
return true
|
||||
},
|
||||
lineStyle: {
|
||||
color: dotColor.value
|
||||
}
|
||||
},
|
||||
axisPointer: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: primaryColor(1),
|
||||
width: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
formatter(value, idx) {
|
||||
if (idx === 0) return value
|
||||
return `${value}k`
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: 'dashed',
|
||||
color: dotColor.value
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter(params) {
|
||||
const [firstElement] = params
|
||||
return `<div>
|
||||
<p class="tooltip-title">${firstElement.axisValueLabel}</p>
|
||||
<div class="content-panel"><span>总内容量</span><span class="tooltip-value">${(
|
||||
Number(firstElement.value) * 10000
|
||||
).toLocaleString()}</span></div>
|
||||
</div>`
|
||||
},
|
||||
className: 'echarts-tooltip-diy'
|
||||
},
|
||||
graphic: {
|
||||
elements: graphicElements.value
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: chartsData.value,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
// symbol: 'circle',
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: new graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.5)
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: primaryColor(0.57)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(1)
|
||||
}
|
||||
])
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.13)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(0.03)
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<template>
|
||||
<Chart :height="height" :option="chartOption" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Chart from '@/components/charts/index.vue'
|
||||
import useChartOption from '@/hooks/charts'
|
||||
import { graphic } from 'echarts'
|
||||
import { ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useThemeStore } from '@/pinia'
|
||||
import { addOpacityToColor } from '@/theme/color'
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
|
||||
const prop = defineProps({
|
||||
height: {
|
||||
type: String,
|
||||
default: '128px'
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const graphicFactory = (side) => {
|
||||
return {
|
||||
type: 'text',
|
||||
bottom: '8',
|
||||
...side,
|
||||
style: {
|
||||
text: '',
|
||||
textAlign: 'center',
|
||||
fill: themeStore.isDark ? '#FFFFFF' : '#000000',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
const graphicElements = ref([
|
||||
graphicFactory({ left: '5%' }),
|
||||
graphicFactory({ right: 0 })
|
||||
])
|
||||
const primaryColor = (opacity) => addOpacityToColor(settings.value.themeColor, opacity)
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: '40',
|
||||
right: '0',
|
||||
top: '10',
|
||||
bottom: '30'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
offset: 2,
|
||||
show: false,
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
show: false,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
graphic: {
|
||||
elements: graphicElements.value
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: prop.data,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbolSize: 12,
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
itemStyle: {
|
||||
borderWidth: 2
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: new graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.2)
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: primaryColor(0.39)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(1)
|
||||
}
|
||||
])
|
||||
},
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
opacity: 0.8,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: primaryColor(0.13)
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: primaryColor(0.03)
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<template>
|
||||
<div class="">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div v-if="title" class="text-sm font-semibold tracking-tight text-base-text">
|
||||
{{ title }}
|
||||
</div>
|
||||
<slot v-else name="title" />
|
||||
</div>
|
||||
<div class="w-full relative">
|
||||
<div v-if="type !== 4">
|
||||
<div class="mt-4 text-3xl font-mono text-base-text">
|
||||
<el-statistic :value="268500" />
|
||||
</div>
|
||||
<div class="mt-2 text-xs font-mono text-muted-foreground">
|
||||
+80% <el-icon class="align-middle"><TopRight /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute top-0 right-2 w-[50%] h-20">
|
||||
<charts-people-number v-if="type === 1" :data="data[0]" height="100%" />
|
||||
<charts-people-number v-if="type === 2" :data="data[1]" height="100%" />
|
||||
<charts-people-number v-if="type === 3" :data="data[2]" height="100%" />
|
||||
</div>
|
||||
<charts-content-number v-if="type === 4" height="14rem" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import chartsPeopleNumber from './charts-people-numbers.vue'
|
||||
import chartsContentNumber from './charts-content-numbers.vue'
|
||||
defineProps({
|
||||
type: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const data = [
|
||||
[12, 22, 32, 45, 32, 78, 89, 92],
|
||||
[1, 2, 43, 5, 67, 78, 89, 12],
|
||||
[12, 22, 32, 45, 32, 78, 89, 92]
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
import KraCard from './card.vue'
|
||||
import KraChart from './charts.vue'
|
||||
import KraNotice from './notice.vue'
|
||||
import KraQuickLink from './quickLinks.vue'
|
||||
|
||||
export {
|
||||
KraCard,
|
||||
KraChart,
|
||||
KraNotice,
|
||||
KraQuickLink,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,37 +30,20 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const notices = [
|
||||
{
|
||||
typeTitle: '通知',
|
||||
time: '今天',
|
||||
title: 'Kra 管理后台已完成核心系统、公告和邮件模块接入。',
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getInfoList } from '@/modules/announcement/api/info'
|
||||
const notices = ref([])
|
||||
onMounted(async () => {
|
||||
const response = await getInfoList({ page: 1, pageSize: 8, status: 1 })
|
||||
if (response.code !== 0) return
|
||||
notices.value = (response.data?.list || []).map((item) => ({
|
||||
typeTitle: item.type === 2 ? '公告' : '通知',
|
||||
time: item.createdAt ? new Date(item.createdAt).toLocaleDateString('zh-CN') : '',
|
||||
title: item.title,
|
||||
dotClass: 'bg-cyan-500',
|
||||
tagClass: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/40 dark:text-cyan-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '安全',
|
||||
time: '2天前',
|
||||
title: '请在部署前修改 JWT 密钥并配置数据库连接信息。',
|
||||
dotClass: 'bg-emerald-500',
|
||||
tagClass: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '运维',
|
||||
time: '3天前',
|
||||
title: '邮件告警默认关闭,填写 SMTP 配置后自动启用。',
|
||||
dotClass: 'bg-amber-500',
|
||||
tagClass: 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-200'
|
||||
},
|
||||
{
|
||||
typeTitle: '存储',
|
||||
time: '5天前',
|
||||
title: '媒体文件默认存储在本地 uploads/file 目录。',
|
||||
dotClass: 'bg-violet-500',
|
||||
tagClass: 'bg-violet-100 text-violet-700 dark:bg-violet-900/40 dark:text-violet-200'
|
||||
}
|
||||
]
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
|
|||
|
|
@ -1,81 +1,38 @@
|
|||
<template>
|
||||
<template>
|
||||
<div class="h-full kra-container2 overflow-auto bg-main">
|
||||
<div class="space-y-2 py-2">
|
||||
<kra-card
|
||||
class="relative overflow-hidden rounded-xl border border-slate-200/80 bg-white px-5 py-6 shadow-sm dark:border-slate-700 dark:bg-slate-900"
|
||||
>
|
||||
|
||||
<div class="relative flex flex-col gap-2 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs tracking-[0.2em] text-muted-foreground">DASHBOARD</p>
|
||||
<h1 class="mt-2 text-xl font-semibold text-base-text lg:text-2xl">
|
||||
欢迎回来,开始今天的Coding节奏
|
||||
</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground">
|
||||
{{ today }} · 已为你聚合核心业务数据和系统公告
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<kra-card class="rounded-xl border border-slate-200/80 bg-white px-5 py-6 shadow-sm dark:border-slate-700 dark:bg-slate-900">
|
||||
<p class="text-xs tracking-[0.2em] text-muted-foreground">DASHBOARD</p>
|
||||
<h1 class="mt-2 text-xl font-semibold text-base-text lg:text-2xl">欢迎回来,{{ userStore.userInfo.nickName }}</h1>
|
||||
<p class="mt-2 text-sm text-muted-foreground">{{ today }} · 当前展示后台实时数据</p>
|
||||
</kra-card>
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3">
|
||||
<kra-card>
|
||||
<kra-chart :type="1" title="访问人数" />
|
||||
</kra-card>
|
||||
<kra-card>
|
||||
<kra-chart :type="2" title="新增客户" />
|
||||
</kra-card>
|
||||
<kra-card>
|
||||
<kra-chart :type="3" title="解决数量" />
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<kra-card v-for="item in statistics" :key="item.title">
|
||||
<div class="p-2"><div class="text-sm text-muted-foreground">{{ item.title }}</div><div class="mt-3 text-3xl font-semibold text-base-text">{{ item.value }}</div></div>
|
||||
</kra-card>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-stretch gap-2 xl:grid-cols-12">
|
||||
<div class="grid grid-cols-1 gap-2 content-start xl:col-span-8 xl:h-full">
|
||||
<kra-card title="内容数据">
|
||||
<kra-chart :type="4" />
|
||||
</kra-card>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2 xl:col-span-4 xl:h-full">
|
||||
<kra-card title="快捷功能" show-action custom-class="min-h-[300px]">
|
||||
<kra-quick-link />
|
||||
</kra-card>
|
||||
<kra-card title="公告" show-action custom-class="min-h-[300px]">
|
||||
<kra-notice />
|
||||
</kra-card>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 items-stretch gap-2 xl:grid-cols-2">
|
||||
<kra-card title="快捷功能" show-action custom-class="min-h-[300px]"><kra-quick-link /></kra-card>
|
||||
<kra-card title="公告" show-action custom-class="min-h-[300px]"><kra-notice /></kra-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import {
|
||||
KraChart,
|
||||
KraNotice,
|
||||
KraQuickLink,
|
||||
KraCard
|
||||
} from './components'
|
||||
|
||||
const today = computed(() => {
|
||||
try {
|
||||
const d = new Date()
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit'
|
||||
})
|
||||
} catch (e) {
|
||||
return new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
})
|
||||
|
||||
defineOptions({
|
||||
name: 'Dashboard'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useUserStore } from '@/pinia/modules/user'
|
||||
import { getUserList } from '@/api/user'
|
||||
import { getSysOperationRecordList } from '@/api/sysOperationRecord'
|
||||
import { getFileList } from '@/api/fileUploadAndDownload'
|
||||
import { getInfoList } from '@/modules/announcement/api/info'
|
||||
import { KraNotice, KraQuickLink, KraCard } from './components'
|
||||
defineOptions({ name: 'Dashboard' })
|
||||
const userStore = useUserStore()
|
||||
const today = computed(() => new Date().toLocaleDateString('zh-CN'))
|
||||
const statistics = ref([{ title: '系统用户', value: '—' }, { title: '操作记录', value: '—' }, { title: '媒体文件', value: '—' }, { title: '系统公告', value: '—' }])
|
||||
onMounted(async () => {
|
||||
const results = await Promise.allSettled([getUserList({ page: 1, pageSize: 1 }), getSysOperationRecordList({ page: 1, pageSize: 1 }), getFileList({ page: 1, pageSize: 1 }), getInfoList({ page: 1, pageSize: 1 })])
|
||||
results.forEach((result, index) => { if (result.status === 'fulfilled' && result.value?.code === 0) statistics.value[index].value = result.value.data?.total ?? 0 })
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
<template>
|
||||
<!-- 工具按钮组:扁平图标,无独立描边/阴影,hover 时图标 morph 为文案(取代 tooltip)。 -->
|
||||
<div class="flex items-center gap-1 mx-3">
|
||||
<g-dropdown-menu
|
||||
v-if="isDev"
|
||||
trigger="hover"
|
||||
:items="videoList"
|
||||
@select="(item) => toDoc(item.value)"
|
||||
>
|
||||
<icon-button icon="lucide:clapperboard" label="教程" />
|
||||
</g-dropdown-menu>
|
||||
|
||||
<icon-button
|
||||
v-if="settings.header.search.visible"
|
||||
icon="lucide:search"
|
||||
|
|
@ -50,8 +41,6 @@
|
|||
import { emitter } from '@/utils/bus.js'
|
||||
import CommandMenu from '@/components/commandMenu/index.vue'
|
||||
import IconButton from '@/components/iconButton/index.vue'
|
||||
import { toDoc } from '@/utils/doc'
|
||||
import { isDev } from '@/utils/env.js'
|
||||
|
||||
const themeStore = useThemeStore()
|
||||
const { settings } = storeToRefs(themeStore)
|
||||
|
|
@ -95,62 +84,4 @@
|
|||
|
||||
initPage()
|
||||
|
||||
const videoList = [
|
||||
{
|
||||
label: '1.clone项目和安装依赖',
|
||||
value: 'https://www.bilibili.com/video/BV1jx4y1s7xx'
|
||||
},
|
||||
{
|
||||
label: '2.初始化项目',
|
||||
value: 'https://www.bilibili.com/video/BV1sr421K7sv'
|
||||
},
|
||||
{
|
||||
label: '3.开启调试工具+创建初始化包',
|
||||
value: 'https://www.bilibili.com/video/BV1iH4y1c7Na'
|
||||
},
|
||||
{
|
||||
label: '4.手动使用自动化创建功能',
|
||||
value: 'https://www.bilibili.com/video/BV1UZ421T7fV'
|
||||
},
|
||||
{
|
||||
label: '5.使用已有表格创建业务',
|
||||
value: 'https://www.bilibili.com/video/BV1NE4m1977s'
|
||||
},
|
||||
{
|
||||
label: '6.使用AI创建业务和创建数据源模式的可选项',
|
||||
value: 'https://www.bilibili.com/video/BV17i421a7DE'
|
||||
},
|
||||
{
|
||||
label: '7.创建自己的后端方法',
|
||||
value: 'https://www.bilibili.com/video/BV1Yw4m1k7fg'
|
||||
},
|
||||
{
|
||||
label: '8.新增一个前端页面',
|
||||
value: 'https://www.bilibili.com/video/BV12y411i7oE'
|
||||
},
|
||||
{
|
||||
label: '9.配置一个前端二级页面',
|
||||
value: 'https://www.bilibili.com/video/BV1ZM4m1y7i3'
|
||||
},
|
||||
{
|
||||
label: '10.配置一个前端菜单参数',
|
||||
value: 'https://www.bilibili.com/video/BV1WS42197DZ'
|
||||
},
|
||||
{
|
||||
label: '11.菜单参数实战+动态菜单标题+菜单高亮配置',
|
||||
value: 'https://www.bilibili.com/video/BV1NE4m1979c'
|
||||
},
|
||||
{
|
||||
label: '12.增加菜单可控按钮',
|
||||
value: 'https://www.bilibili.com/video/BV1Sw4m1k746'
|
||||
},
|
||||
{
|
||||
label: '14.新增客户角色和其相关配置教学',
|
||||
value: 'https://www.bilibili.com/video/BV1Ki421a7X2'
|
||||
},
|
||||
{
|
||||
label: '15.发布项目上线',
|
||||
value: 'https://www.bilibili.com/video/BV1Lx4y1s77D'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,36 @@
|
|||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="对象存储" name="storage">
|
||||
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
|
||||
<el-form-item label="存储类型" class="md:col-span-2">
|
||||
<el-select v-model="config.admin.storage.type" class="!w-full">
|
||||
<el-option v-for="item in storageTypes" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<template v-if="config.admin.storage.type === 'qiniu'">
|
||||
<el-form-item label="区域"><el-input v-model.trim="config.admin.storage.qiniu.zone" placeholder="ZoneHuadong" /></el-form-item>
|
||||
<el-form-item label="Bucket"><el-input v-model.trim="config.admin.storage.qiniu.bucket" /></el-form-item>
|
||||
<el-form-item label="访问域名"><el-input v-model.trim="config.admin.storage.qiniu.base_url" /></el-form-item>
|
||||
<el-form-item label="Access Key"><el-input v-model.trim="config.admin.storage.qiniu.access_key" /></el-form-item>
|
||||
<el-form-item label="Secret Key"><el-input v-model="config.admin.storage.qiniu.secret_key" show-password /></el-form-item>
|
||||
<el-form-item label="连接选项"><div class="flex gap-5"><el-switch v-model="config.admin.storage.qiniu.use_https" active-text="HTTPS" /><el-switch v-model="config.admin.storage.qiniu.use_cdn_domains" active-text="CDN 域名" /></div></el-form-item>
|
||||
</template>
|
||||
<template v-else-if="currentObjectStorage">
|
||||
<el-form-item label="Endpoint"><el-input v-model.trim="currentObjectStorage.endpoint" placeholder="可留空使用提供商默认地址" /></el-form-item>
|
||||
<el-form-item label="Region"><el-input v-model.trim="currentObjectStorage.region" /></el-form-item>
|
||||
<el-form-item label="Bucket"><el-input v-model.trim="currentObjectStorage.bucket" /></el-form-item>
|
||||
<el-form-item label="访问域名"><el-input v-model.trim="currentObjectStorage.base_url" /></el-form-item>
|
||||
<el-form-item label="Access Key"><el-input v-model.trim="currentObjectStorage.access_key" /></el-form-item>
|
||||
<el-form-item label="Secret Key"><el-input v-model="currentObjectStorage.secret_key" show-password /></el-form-item>
|
||||
<el-form-item label="对象前缀"><el-input v-model.trim="currentObjectStorage.path_prefix" /></el-form-item>
|
||||
<el-form-item v-if="config.admin.storage.type === 'cloudflare-r2'" label="Account ID"><el-input v-model.trim="currentObjectStorage.account_id" /></el-form-item>
|
||||
<el-form-item label="连接选项"><div class="flex gap-5"><el-switch v-model="currentObjectStorage.use_ssl" active-text="HTTPS" /><el-switch v-model="currentObjectStorage.force_path_style" active-text="Path Style" /></div></el-form-item>
|
||||
</template>
|
||||
<p class="md:col-span-2 text-sm text-gray-500">对象存储客户端在服务启动时创建,保存后需重启服务生效。</p>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="邮件设置" name="email">
|
||||
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
|
||||
<el-form-item label="默认收件人">
|
||||
|
|
@ -68,7 +98,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getSystemConfig, reloadSystem, setSystemConfig } from '@/api/system'
|
||||
import { emailTest } from '@/api/email'
|
||||
|
|
@ -82,13 +112,22 @@
|
|||
admin: {
|
||||
routerPrefix: '',
|
||||
jwt: { signingKey: '******', issuer: 'kra' },
|
||||
local: { storePath: 'uploads/file', pathPrefix: 'uploads/file' }
|
||||
local: { storePath: 'uploads/file', pathPrefix: 'uploads/file' },
|
||||
storage: { type: 'local', qiniu: {}, aliyun_oss: {}, huawei_obs: {}, tencent_cos: {}, aws_s3: {}, cloudflare_r2: {}, minio: {} }
|
||||
},
|
||||
email: {
|
||||
to: '', from: '', host: '', secret: '******', nickname: '', port: 465,
|
||||
'is-ssl': true, 'is-loginauth': false
|
||||
}
|
||||
})
|
||||
const storageTypes = [
|
||||
{ label: '本地存储', value: 'local' }, { label: '七牛云', value: 'qiniu' },
|
||||
{ label: '阿里云 OSS', value: 'aliyun-oss' }, { label: '华为云 OBS', value: 'huawei-obs' },
|
||||
{ label: '腾讯云 COS', value: 'tencent-cos' }, { label: 'AWS S3', value: 'aws-s3' },
|
||||
{ label: 'Cloudflare R2', value: 'cloudflare-r2' }, { label: 'MinIO', value: 'minio' }
|
||||
]
|
||||
const storageKeys = { 'aliyun-oss': 'aliyun_oss', 'huawei-obs': 'huawei_obs', 'tencent-cos': 'tencent_cos', 'aws-s3': 'aws_s3', 'cloudflare-r2': 'cloudflare_r2', minio: 'minio' }
|
||||
const currentObjectStorage = computed(() => config.value.admin.storage[storageKeys[config.value.admin.storage.type]])
|
||||
|
||||
const initForm = async () => {
|
||||
const res = await getSystemConfig()
|
||||
|
|
@ -97,6 +136,15 @@
|
|||
admin: { ...config.value.admin, ...res.data.config.admin,
|
||||
jwt: { ...config.value.admin.jwt, ...res.data.config.admin?.jwt },
|
||||
local: { ...config.value.admin.local, ...res.data.config.admin?.local }
|
||||
, storage: { ...config.value.admin.storage, ...res.data.config.admin?.storage,
|
||||
qiniu: { ...config.value.admin.storage.qiniu, ...res.data.config.admin?.storage?.qiniu },
|
||||
aliyun_oss: { ...config.value.admin.storage.aliyun_oss, ...res.data.config.admin?.storage?.aliyun_oss },
|
||||
huawei_obs: { ...config.value.admin.storage.huawei_obs, ...res.data.config.admin?.storage?.huawei_obs },
|
||||
tencent_cos: { ...config.value.admin.storage.tencent_cos, ...res.data.config.admin?.storage?.tencent_cos },
|
||||
aws_s3: { ...config.value.admin.storage.aws_s3, ...res.data.config.admin?.storage?.aws_s3 },
|
||||
cloudflare_r2: { ...config.value.admin.storage.cloudflare_r2, ...res.data.config.admin?.storage?.cloudflare_r2 },
|
||||
minio: { ...config.value.admin.storage.minio, ...res.data.config.admin?.storage?.minio }
|
||||
}
|
||||
},
|
||||
email: { ...config.value.email, ...res.data.config.email }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ export default defineConfig({
|
|||
//(如 button 的 buttonVariants),不显式纳入扫描的话,只出现在这些 .js 里的原子类
|
||||
//(bg-primary / bg-error / hover:bg-primary-600 ...)不会被生成,按钮就会丢失主题色。
|
||||
// 这里在保留默认文件类型的前提下,额外把 core/componentLibrary 下的 .js/.ts 纳入扫描。
|
||||
// 详见 aiDoc/frontend-backend/component-library.md。
|
||||
content: {
|
||||
pipeline: {
|
||||
include: [
|
||||
|
|
|
|||
Loading…
Reference in New Issue