Merge ade00eed1c
into fdd7bf41c0
This commit is contained in:
commit
7859e9dd98
@ -107,6 +107,7 @@ var requestInterval, _ = strconv.Atoi(os.Getenv("POLLING_INTERVAL"))
|
|||||||
var RequestInterval = time.Duration(requestInterval) * time.Second
|
var RequestInterval = time.Duration(requestInterval) * time.Second
|
||||||
|
|
||||||
var SyncFrequency = env.Int("SYNC_FREQUENCY", 10*60) // unit is second
|
var SyncFrequency = env.Int("SYNC_FREQUENCY", 10*60) // unit is second
|
||||||
|
var TimerFrequency = env.Int("TIMER_FREQUENCY", 24) // unit is hour
|
||||||
|
|
||||||
var BatchUpdateEnabled = false
|
var BatchUpdateEnabled = false
|
||||||
var BatchUpdateInterval = env.Int("BATCH_UPDATE_INTERVAL", 5)
|
var BatchUpdateInterval = env.Int("BATCH_UPDATE_INTERVAL", 5)
|
||||||
|
3
go.mod
3
go.mod
@ -55,6 +55,7 @@ require (
|
|||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/go-co-op/gocron v1.37.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
@ -87,6 +88,7 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/smarty/assertions v1.15.0 // indirect
|
github.com/smarty/assertions v1.15.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
@ -96,6 +98,7 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/net v0.26.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
golang.org/x/oauth2 v0.21.0 // indirect
|
||||||
|
22
go.sum
22
go.sum
@ -31,6 +31,7 @@ github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4
|
|||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
@ -67,6 +68,8 @@ github.com/gin-contrib/static v1.1.2 h1:c3kT4bFkUJn2aoRU3s6XnMjJT8J6nNWJkR0Nglqm
|
|||||||
github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw=
|
github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
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/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
|
||||||
|
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
@ -116,6 +119,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.4.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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||||
@ -156,7 +160,12 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
|||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
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.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
@ -177,6 +186,7 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
|||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
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/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkoukk/tiktoken-go v0.1.7 h1:qOBHXX4PHtvIvmOtyg1EeKlwFRiMKAcoMp4Q+bLQDmw=
|
github.com/pkoukk/tiktoken-go v0.1.7 h1:qOBHXX4PHtvIvmOtyg1EeKlwFRiMKAcoMp4Q+bLQDmw=
|
||||||
@ -184,7 +194,12 @@ github.com/pkoukk/tiktoken-go v0.1.7/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYde
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
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 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||||
@ -198,6 +213,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/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=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
@ -217,6 +233,8 @@ go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGX
|
|||||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
@ -266,6 +284,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.187.0 h1:Mxs7VATVC2v7CY+7Xwm4ndkX71hpElcvx0D1Ji/p1eo=
|
google.golang.org/api v0.187.0 h1:Mxs7VATVC2v7CY+7Xwm4ndkX71hpElcvx0D1Ji/p1eo=
|
||||||
google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk=
|
google.golang.org/api v0.187.0/go.mod h1:KIHlTc4x7N7gKKuVsdmfBXN13yEEWXWFURWY6SBp2gk=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@ -296,7 +315,10 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
3
main.go
3
main.go
@ -73,6 +73,8 @@ func main() {
|
|||||||
go model.SyncOptions(config.SyncFrequency)
|
go model.SyncOptions(config.SyncFrequency)
|
||||||
go model.SyncChannelCache(config.SyncFrequency)
|
go model.SyncChannelCache(config.SyncFrequency)
|
||||||
}
|
}
|
||||||
|
go model.ScheduleCheckAndDowngrade()
|
||||||
|
|
||||||
if os.Getenv("CHANNEL_TEST_FREQUENCY") != "" {
|
if os.Getenv("CHANNEL_TEST_FREQUENCY") != "" {
|
||||||
frequency, err := strconv.Atoi(os.Getenv("CHANNEL_TEST_FREQUENCY"))
|
frequency, err := strconv.Atoi(os.Getenv("CHANNEL_TEST_FREQUENCY"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -112,4 +114,5 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.FatalLog("failed to start HTTP server: " + err.Error())
|
logger.FatalLog("failed to start HTTP server: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/go-co-op/gocron"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
billingratio "github.com/songquanpeng/one-api/relay/billing/ratio"
|
billingratio "github.com/songquanpeng/one-api/relay/billing/ratio"
|
||||||
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -79,6 +81,21 @@ func InitOptionMap() {
|
|||||||
loadOptionsFromDatabase()
|
loadOptionsFromDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ScheduleCheckAndDowngrade() {
|
||||||
|
s := gocron.NewScheduler(time.UTC)
|
||||||
|
|
||||||
|
// 设置每天0点执行
|
||||||
|
_, err := s.Every(1).Day().At("00:00").Do(checkAndDowngradeUsers)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("创建调度任务失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始调度
|
||||||
|
s.StartBlocking()
|
||||||
|
log.Printf("开始调度")
|
||||||
|
}
|
||||||
|
|
||||||
func loadOptionsFromDatabase() {
|
func loadOptionsFromDatabase() {
|
||||||
options, _ := AllOption()
|
options, _ := AllOption()
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
|
@ -10,7 +10,9 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/common/random"
|
"github.com/songquanpeng/one-api/common/random"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,6 +50,7 @@ type User struct {
|
|||||||
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
|
Group string `json:"group" gorm:"type:varchar(32);default:'default'"`
|
||||||
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
AffCode string `json:"aff_code" gorm:"type:varchar(32);column:aff_code;uniqueIndex"`
|
||||||
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
InviterId int `json:"inviter_id" gorm:"type:int;column:inviter_id;index"`
|
||||||
|
ExpirationDate int64 `json:"expiration_date" gorm:"bigint;default:0;column:expiration_date"` // Expiration date of the user's subscription or account.
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMaxUserId() int {
|
func GetMaxUserId() int {
|
||||||
@ -211,6 +214,25 @@ func (user *User) ValidateAndFill() (err error) {
|
|||||||
if !okay || user.Status != UserStatusEnabled {
|
if !okay || user.Status != UserStatusEnabled {
|
||||||
return errors.New("用户名或密码错误,或用户已被封禁")
|
return errors.New("用户名或密码错误,或用户已被封禁")
|
||||||
}
|
}
|
||||||
|
// 校验用户是不是非default,如果是非default,判断到期时间如果过期了降级为default
|
||||||
|
if !(user.ExpirationDate > 0 && user.Username == "root") {
|
||||||
|
// 将时间戳转换为 time.Time 类型
|
||||||
|
expirationTime := time.Unix(user.ExpirationDate, 0)
|
||||||
|
// 获取当前时间
|
||||||
|
currentTime := time.Now()
|
||||||
|
|
||||||
|
// 比较当前时间和到期时间
|
||||||
|
if expirationTime.Before(currentTime) {
|
||||||
|
// 降级为 default
|
||||||
|
user.Group = "default"
|
||||||
|
err := DB.Model(user).Updates(user).Error
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("用户: %s, 降级为 default 时发生错误: %v\n", user.Username, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("用户: %s, 特权组过期降为 default\n", user.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,3 +470,48 @@ func GetUsernameById(id int) (username string) {
|
|||||||
DB.Model(&User{}).Where("id = ?", id).Select("username").Find(&username)
|
DB.Model(&User{}).Where("id = ?", id).Select("username").Find(&username)
|
||||||
return username
|
return username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkAndDowngradeUsers() {
|
||||||
|
// 获取昨天的时间戳
|
||||||
|
yesterdayTimestamp := time.Now().AddDate(0, 0, -1).Unix()
|
||||||
|
|
||||||
|
// 获取需要降级的用户ID列表
|
||||||
|
var userList []int
|
||||||
|
query := DB.Model(&User{}).
|
||||||
|
Where("`Group` != ?", "default").
|
||||||
|
Where("`username` != ?", "root").
|
||||||
|
Where("`expiration_date` > 0").
|
||||||
|
Where("`expiration_date` <= ?", yesterdayTimestamp).
|
||||||
|
Select("id").
|
||||||
|
Find(&userList)
|
||||||
|
|
||||||
|
// 处理查询错误
|
||||||
|
if query.Error != nil {
|
||||||
|
log.Printf("查询用户列表失败: %v", query.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有用户需要降级,直接返回
|
||||||
|
if len(userList) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量降级用户
|
||||||
|
updateQuery := DB.Model(&User{}).Where("id IN ?", userList).Update("Group", "default")
|
||||||
|
|
||||||
|
// 处理更新错误
|
||||||
|
if updateQuery.Error != nil {
|
||||||
|
log.Printf("批量更新用户分组失败: %v", updateQuery.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除已过期用户的Redis缓存
|
||||||
|
if common.RedisEnabled {
|
||||||
|
for _, userId := range userList {
|
||||||
|
err := common.RedisSet(fmt.Sprintf("user_group:%d", userId), "default", time.Duration(UserId2GroupCacheSeconds)*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("更新用户: %d, 权益缓存失败, Error: %v", userId, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"@tabler/icons-react": "^2.44.0",
|
"@tabler/icons-react": "^2.44.0",
|
||||||
"apexcharts": "3.35.3",
|
"apexcharts": "3.35.3",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"date-fns": "^3.6.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"framer-motion": "^6.3.16",
|
"framer-motion": "^6.3.16",
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-apexcharts": "1.4.0",
|
"react-apexcharts": "1.4.0",
|
||||||
|
"react-datepicker": "^7.3.0",
|
||||||
"react-device-detect": "^2.2.2",
|
"react-device-detect": "^2.2.2",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
|
@ -2,7 +2,9 @@ import PropTypes from 'prop-types';
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
@ -17,7 +19,11 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
IconButton,
|
IconButton,
|
||||||
FormHelperText
|
FormHelperText,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
|
Switch,
|
||||||
|
FormControlLabel
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import Visibility from '@mui/icons-material/Visibility';
|
import Visibility from '@mui/icons-material/Visibility';
|
||||||
@ -44,6 +50,17 @@ const validationSchema = Yup.object().shape({
|
|||||||
is: false,
|
is: false,
|
||||||
then: Yup.number().min(0, '额度 不能小于 0'),
|
then: Yup.number().min(0, '额度 不能小于 0'),
|
||||||
otherwise: Yup.number()
|
otherwise: Yup.number()
|
||||||
|
}),
|
||||||
|
expiration_date: Yup.mixed().when('group', {
|
||||||
|
is: (group) => group !== 'default',
|
||||||
|
then: Yup.mixed().test(
|
||||||
|
'expiration_date-required',
|
||||||
|
'到期时间 不能为空',
|
||||||
|
function (value) {
|
||||||
|
const { expiration_date } = this.parent;
|
||||||
|
return expiration_date === -1 || !!expiration_date;
|
||||||
|
}
|
||||||
|
),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -53,7 +70,8 @@ const originInputs = {
|
|||||||
display_name: '',
|
display_name: '',
|
||||||
password: '',
|
password: '',
|
||||||
group: 'default',
|
group: 'default',
|
||||||
quota: 0
|
quota: 0,
|
||||||
|
expiration_date: null
|
||||||
};
|
};
|
||||||
|
|
||||||
const EditModal = ({ open, userId, onCancel, onOk }) => {
|
const EditModal = ({ open, userId, onCancel, onOk }) => {
|
||||||
@ -65,6 +83,12 @@ const EditModal = ({ open, userId, onCancel, onOk }) => {
|
|||||||
const submit = async (values, { setErrors, setStatus, setSubmitting }) => {
|
const submit = async (values, { setErrors, setStatus, setSubmitting }) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
|
||||||
|
// 将到期时间转换为 Unix 时间戳
|
||||||
|
if (values.expiration_date && values.expiration_date !== -1) {
|
||||||
|
const date = new Date(values.expiration_date);
|
||||||
|
values.expiration_date = Math.floor(date.getTime() / 1000); // 转换为秒级的 Unix 时间戳
|
||||||
|
}
|
||||||
|
|
||||||
let res;
|
let res;
|
||||||
if (values.is_edit) {
|
if (values.is_edit) {
|
||||||
res = await API.put(`/api/user/`, { ...values, id: parseInt(userId) });
|
res = await API.put(`/api/user/`, { ...values, id: parseInt(userId) });
|
||||||
@ -95,16 +119,23 @@ const EditModal = ({ open, userId, onCancel, onOk }) => {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadUser = async () => {
|
const loadUser = useCallback(async () => {
|
||||||
let res = await API.get(`/api/user/${userId}`);
|
let res = await API.get(`/api/user/${userId}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
data.is_edit = true;
|
data.is_edit = true;
|
||||||
|
|
||||||
|
// 将 Unix 时间戳转换为日期字符串
|
||||||
|
if (data.expiration_date && data.expiration_date !== -1) {
|
||||||
|
const date = new Date(data.expiration_date * 1000); // 转换为毫秒级的时间戳
|
||||||
|
data.expiration_date = format(date, 'yyyy-MM-dd'); // 格式化为 date 格式
|
||||||
|
}
|
||||||
|
|
||||||
setInputs(data);
|
setInputs(data);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
};
|
}, [userId]);
|
||||||
|
|
||||||
const fetchGroups = async () => {
|
const fetchGroups = async () => {
|
||||||
try {
|
try {
|
||||||
@ -122,159 +153,203 @@ const EditModal = ({ open, userId, onCancel, onOk }) => {
|
|||||||
} else {
|
} else {
|
||||||
setInputs(originInputs);
|
setInputs(originInputs);
|
||||||
}
|
}
|
||||||
}, [userId]);
|
}, [userId, loadUser]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onClose={onCancel} fullWidth maxWidth={'md'}>
|
<Dialog open={open} onClose={onCancel} fullWidth maxWidth={'md'}>
|
||||||
<DialogTitle sx={{ margin: '0px', fontWeight: 700, lineHeight: '1.55556', padding: '24px', fontSize: '1.125rem' }}>
|
<DialogTitle sx={{ margin: '0px', fontWeight: 700, lineHeight: '1.55556', padding: '24px', fontSize: '1.125rem' }}>
|
||||||
{userId ? '编辑用户' : '新建用户'}
|
{userId ? '编辑用户' : '新建用户'}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<Divider />
|
<Divider />
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Formik initialValues={inputs} enableReinitialize validationSchema={validationSchema} onSubmit={submit}>
|
<Formik initialValues={inputs} enableReinitialize validationSchema={validationSchema} onSubmit={submit}>
|
||||||
{({ errors, handleBlur, handleChange, handleSubmit, touched, values, isSubmitting }) => (
|
{({ errors, handleBlur, handleChange, handleSubmit, setFieldValue, touched, values, isSubmitting }) => (
|
||||||
<form noValidate onSubmit={handleSubmit}>
|
<form noValidate onSubmit={handleSubmit}>
|
||||||
<FormControl fullWidth error={Boolean(touched.username && errors.username)} sx={{ ...theme.typography.otherInput }}>
|
<FormControl fullWidth error={Boolean(touched.username && errors.username)} sx={{ ...theme.typography.otherInput }}>
|
||||||
<InputLabel htmlFor="channel-username-label">用户名</InputLabel>
|
<InputLabel htmlFor="channel-username-label">用户名</InputLabel>
|
||||||
<OutlinedInput
|
|
||||||
id="channel-username-label"
|
|
||||||
label="用户名"
|
|
||||||
type="text"
|
|
||||||
value={values.username}
|
|
||||||
name="username"
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onChange={handleChange}
|
|
||||||
inputProps={{ autoComplete: 'username' }}
|
|
||||||
aria-describedby="helper-text-channel-username-label"
|
|
||||||
/>
|
|
||||||
{touched.username && errors.username && (
|
|
||||||
<FormHelperText error id="helper-tex-channel-username-label">
|
|
||||||
{errors.username}
|
|
||||||
</FormHelperText>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth error={Boolean(touched.display_name && errors.display_name)} sx={{ ...theme.typography.otherInput }}>
|
|
||||||
<InputLabel htmlFor="channel-display_name-label">显示名称</InputLabel>
|
|
||||||
<OutlinedInput
|
|
||||||
id="channel-display_name-label"
|
|
||||||
label="显示名称"
|
|
||||||
type="text"
|
|
||||||
value={values.display_name}
|
|
||||||
name="display_name"
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onChange={handleChange}
|
|
||||||
inputProps={{ autoComplete: 'display_name' }}
|
|
||||||
aria-describedby="helper-text-channel-display_name-label"
|
|
||||||
/>
|
|
||||||
{touched.display_name && errors.display_name && (
|
|
||||||
<FormHelperText error id="helper-tex-channel-display_name-label">
|
|
||||||
{errors.display_name}
|
|
||||||
</FormHelperText>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormControl fullWidth error={Boolean(touched.password && errors.password)} sx={{ ...theme.typography.otherInput }}>
|
|
||||||
<InputLabel htmlFor="channel-password-label">密码</InputLabel>
|
|
||||||
<OutlinedInput
|
|
||||||
id="channel-password-label"
|
|
||||||
label="密码"
|
|
||||||
type={showPassword ? 'text' : 'password'}
|
|
||||||
value={values.password}
|
|
||||||
name="password"
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onChange={handleChange}
|
|
||||||
inputProps={{ autoComplete: 'password' }}
|
|
||||||
endAdornment={
|
|
||||||
<InputAdornment position="end">
|
|
||||||
<IconButton
|
|
||||||
aria-label="toggle password visibility"
|
|
||||||
onClick={handleClickShowPassword}
|
|
||||||
onMouseDown={handleMouseDownPassword}
|
|
||||||
edge="end"
|
|
||||||
size="large"
|
|
||||||
>
|
|
||||||
{showPassword ? <Visibility /> : <VisibilityOff />}
|
|
||||||
</IconButton>
|
|
||||||
</InputAdornment>
|
|
||||||
}
|
|
||||||
aria-describedby="helper-text-channel-password-label"
|
|
||||||
/>
|
|
||||||
{touched.password && errors.password && (
|
|
||||||
<FormHelperText error id="helper-tex-channel-password-label">
|
|
||||||
{errors.password}
|
|
||||||
</FormHelperText>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
{values.is_edit && (
|
|
||||||
<>
|
|
||||||
<FormControl fullWidth error={Boolean(touched.quota && errors.quota)} sx={{ ...theme.typography.otherInput }}>
|
|
||||||
<InputLabel htmlFor="channel-quota-label">额度</InputLabel>
|
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
id="channel-quota-label"
|
id="channel-username-label"
|
||||||
label="额度"
|
label="用户名"
|
||||||
type="number"
|
type="text"
|
||||||
value={values.quota}
|
value={values.username}
|
||||||
name="quota"
|
name="username"
|
||||||
endAdornment={<InputAdornment position="end">{renderQuotaWithPrompt(values.quota)}</InputAdornment>}
|
onBlur={handleBlur}
|
||||||
onBlur={handleBlur}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
inputProps={{ autoComplete: 'username' }}
|
||||||
aria-describedby="helper-text-channel-quota-label"
|
aria-describedby="helper-text-channel-username-label"
|
||||||
disabled={values.unlimited_quota}
|
|
||||||
/>
|
/>
|
||||||
|
{touched.username && errors.username && (
|
||||||
{touched.quota && errors.quota && (
|
<FormHelperText error id="helper-tex-channel-username-label">
|
||||||
<FormHelperText error id="helper-tex-channel-quota-label">
|
{errors.username}
|
||||||
{errors.quota}
|
</FormHelperText>
|
||||||
</FormHelperText>
|
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl fullWidth error={Boolean(touched.group && errors.group)} sx={{ ...theme.typography.otherInput }}>
|
<FormControl fullWidth error={Boolean(touched.display_name && errors.display_name)} sx={{ ...theme.typography.otherInput }}>
|
||||||
<InputLabel htmlFor="channel-group-label">分组</InputLabel>
|
<InputLabel htmlFor="channel-display_name-label">显示名称</InputLabel>
|
||||||
<Select
|
<OutlinedInput
|
||||||
id="channel-group-label"
|
id="channel-display_name-label"
|
||||||
label="分组"
|
label="显示名称"
|
||||||
value={values.group}
|
type="text"
|
||||||
name="group"
|
value={values.display_name}
|
||||||
onBlur={handleBlur}
|
name="display_name"
|
||||||
onChange={handleChange}
|
onBlur={handleBlur}
|
||||||
MenuProps={{
|
onChange={handleChange}
|
||||||
PaperProps: {
|
inputProps={{ autoComplete: 'display_name' }}
|
||||||
style: {
|
aria-describedby="helper-text-channel-display_name-label"
|
||||||
maxHeight: 200
|
/>
|
||||||
}
|
{touched.display_name && errors.display_name && (
|
||||||
|
<FormHelperText error id="helper-tex-channel-display_name-label">
|
||||||
|
{errors.display_name}
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl fullWidth error={Boolean(touched.password && errors.password)} sx={{ ...theme.typography.otherInput }}>
|
||||||
|
<InputLabel htmlFor="channel-password-label">密码</InputLabel>
|
||||||
|
<OutlinedInput
|
||||||
|
id="channel-password-label"
|
||||||
|
label="密码"
|
||||||
|
type={showPassword ? 'text' : 'password'}
|
||||||
|
value={values.password}
|
||||||
|
name="password"
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onChange={handleChange}
|
||||||
|
inputProps={{ autoComplete: 'password' }}
|
||||||
|
endAdornment={
|
||||||
|
<InputAdornment position="end">
|
||||||
|
<IconButton
|
||||||
|
aria-label="toggle password visibility"
|
||||||
|
onClick={handleClickShowPassword}
|
||||||
|
onMouseDown={handleMouseDownPassword}
|
||||||
|
edge="end"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
{showPassword ? <Visibility /> : <VisibilityOff />}
|
||||||
|
</IconButton>
|
||||||
|
</InputAdornment>
|
||||||
}
|
}
|
||||||
}}
|
aria-describedby="helper-text-channel-password-label"
|
||||||
>
|
/>
|
||||||
{groupOptions.map((option) => {
|
{touched.password && errors.password && (
|
||||||
return (
|
<FormHelperText error id="helper-tex-channel-password-label">
|
||||||
<MenuItem key={option} value={option}>
|
{errors.password}
|
||||||
{option}
|
</FormHelperText>
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
{touched.group && errors.group && (
|
|
||||||
<FormHelperText error id="helper-tex-channel-group-label">
|
|
||||||
{errors.group}
|
|
||||||
</FormHelperText>
|
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</>
|
|
||||||
)}
|
{values.is_edit && (
|
||||||
<DialogActions>
|
<>
|
||||||
<Button onClick={onCancel}>取消</Button>
|
<FormControl fullWidth error={Boolean(touched.quota && errors.quota)} sx={{ ...theme.typography.otherInput }}>
|
||||||
<Button disableElevation disabled={isSubmitting} type="submit" variant="contained" color="primary">
|
<InputLabel htmlFor="channel-quota-label">额度</InputLabel>
|
||||||
提交
|
<OutlinedInput
|
||||||
</Button>
|
id="channel-quota-label"
|
||||||
</DialogActions>
|
label="额度"
|
||||||
</form>
|
type="number"
|
||||||
)}
|
value={values.quota}
|
||||||
</Formik>
|
name="quota"
|
||||||
</DialogContent>
|
endAdornment={<InputAdornment position="end">{renderQuotaWithPrompt(values.quota)}</InputAdornment>}
|
||||||
</Dialog>
|
onBlur={handleBlur}
|
||||||
|
onChange={handleChange}
|
||||||
|
aria-describedby="helper-text-channel-quota-label"
|
||||||
|
disabled={values.unlimited_quota}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{touched.quota && errors.quota && (
|
||||||
|
<FormHelperText error id="helper-tex-channel-quota-label">
|
||||||
|
{errors.quota}
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl fullWidth error={Boolean(touched.group && errors.group)} sx={{ ...theme.typography.otherInput }}>
|
||||||
|
<InputLabel htmlFor="channel-group-label">分组</InputLabel>
|
||||||
|
<Select
|
||||||
|
id="channel-group-label"
|
||||||
|
label="分组"
|
||||||
|
value={values.group}
|
||||||
|
name="group"
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleChange(e);
|
||||||
|
if (e.target.value === 'default') {
|
||||||
|
setFieldValue('expiration_date', null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
MenuProps={{
|
||||||
|
PaperProps: {
|
||||||
|
style: {
|
||||||
|
maxHeight: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{groupOptions.map((option) => {
|
||||||
|
return (
|
||||||
|
<MenuItem key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
{touched.group && errors.group && (
|
||||||
|
<FormHelperText error id="helper-tex-channel-group-label">
|
||||||
|
{errors.group}
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
</FormControl>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{values.group !== 'default' && (
|
||||||
|
<FormControl fullWidth error={Boolean(touched.expiration_date && errors.expiration_date)} sx={{ ...theme.typography.otherInput }}>
|
||||||
|
<TextField
|
||||||
|
id="channel-expiration_date-label"
|
||||||
|
label="到期时间"
|
||||||
|
type="date" // 修改为 date
|
||||||
|
value={values.expiration_date}
|
||||||
|
name="expiration_date"
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onChange={handleChange}
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true
|
||||||
|
}}
|
||||||
|
inputProps={{
|
||||||
|
max: '9999-12-31' // 设置最大日期
|
||||||
|
}}
|
||||||
|
aria-describedby="helper-text-channel-expiration_date-label"
|
||||||
|
disabled={values.expiration_date === -1}
|
||||||
|
/>
|
||||||
|
{touched.expiration_date && errors.expiration_date && (
|
||||||
|
<FormHelperText error id="helper-tex-channel-expiration_date-label">
|
||||||
|
{errors.expiration_date}
|
||||||
|
</FormHelperText>
|
||||||
|
)}
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={values.expiration_date === -1}
|
||||||
|
onChange={(e) => setFieldValue('expiration_date', e.target.checked ? -1 : '')}
|
||||||
|
name="permanent"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label="永不过期"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onCancel}>取消</Button>
|
||||||
|
<Button disableElevation disabled={isSubmitting} type="submit" variant="contained" color="primary">
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user