fix(admin): 添加管理操作错误处理及更新模板样式

- 管理后台帖子与用户审核操作中添加失败错误重定向处理
- 管理后台帖子、用户、评论删除操作中添加错误检查及提示
- 用户角色更新操作失败时添加错误重定向
- 用户封禁通知失败时添加相应错误提示
- 登录登出时session保存加入错误处理
- 讨论区上传目录创建失败时显示错误提示
- 移除admin_dashboard.html多余样式及修正侧边栏当前页高亮
- admin_posts.html和admin_users.html添加状态样式动态使用的隐藏span元素
- admin_users.html为角色选择添加label以提升无障碍性
- 升级项目依赖版本,包含gin、gorm、validator等核心库版本更新
This commit is contained in:
2026-02-24 21:14:55 +08:00
parent ddec422813
commit 690b4d5961
26 changed files with 290 additions and 199 deletions

View File

@@ -17,7 +17,9 @@ func main() {
} }
authSvc := services.NewAuthService() authSvc := services.NewAuthService()
authSvc.InitAdmin() if err := authSvc.InitAdmin(); err != nil {
log.Fatalf("Failed to initialize admin: %v", err)
}
r := routes.SetupRouter() r := routes.SetupRouter()

64
go.mod
View File

@@ -1,51 +1,57 @@
module lv8girl module lv8girl
go 1.21 go 1.25.0
require ( require (
github.com/gin-contrib/sessions v1.0.1 github.com/gin-contrib/sessions v1.0.4
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.11.0
github.com/glebarez/sqlite v1.10.0 github.com/glebarez/sqlite v1.11.0
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.48.0
gorm.io/gorm v1.25.8 gorm.io/gorm v1.31.1
) )
require ( require (
github.com/bytedance/sonic v1.11.3 // indirect github.com/bytedance/gopkg v0.1.3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/bytedance/sonic v1.15.0 // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/goccy/go-yaml v1.19.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect github.com/gorilla/sessions v1.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
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.0 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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.3.1 // indirect
golang.org/x/arch v0.7.0 // indirect go.uber.org/mock v0.6.0 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/arch v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/net v0.50.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect golang.org/x/sys v0.41.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect golang.org/x/text v0.34.0 // indirect
modernc.org/libc v1.22.5 // indirect google.golang.org/protobuf v1.36.11 // indirect
modernc.org/mathutil v1.5.0 // indirect modernc.org/libc v1.68.0 // indirect
modernc.org/memory v1.5.0 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/sqlite v1.23.1 // indirect modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.46.1 // indirect
) )

180
go.sum
View File

@@ -1,66 +1,65 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 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/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI= github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM= github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 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/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 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/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 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/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 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/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 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/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -70,11 +69,16 @@ 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/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 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -82,48 +86,70 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.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.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.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 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/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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/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=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ=
modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

View File

@@ -4,9 +4,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"github.com/gin-gonic/gin"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"lv8girl/internal/services" "lv8girl/internal/services"
"github.com/gin-gonic/gin"
) )
type AdminController struct { type AdminController struct {
@@ -50,10 +51,16 @@ func (c *AdminController) ApprovePost(ctx *gin.Context) {
action := ctx.Param("action") action := ctx.Param("action")
if action == "approve" { if action == "approve" {
c.adminSvc.ApprovePost(postID) if err := c.adminSvc.ApprovePost(postID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=审核失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=帖子已通过审核") ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=帖子已通过审核")
} else if action == "reject" { } else if action == "reject" {
c.adminSvc.RejectPost(postID) if err := c.adminSvc.RejectPost(postID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=拒绝失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=帖子已拒绝") ctx.Redirect(http.StatusFound, "/admin/pending_posts?msg=帖子已拒绝")
} else { } else {
ctx.Redirect(http.StatusFound, "/admin/pending_posts") ctx.Redirect(http.StatusFound, "/admin/pending_posts")
@@ -78,12 +85,24 @@ func (c *AdminController) ApproveUser(ctx *gin.Context) {
adminID, _ := ctx.Get("user_id") adminID, _ := ctx.Get("user_id")
if action == "approve" { if action == "approve" {
c.adminSvc.ApproveUser(userID) if err := c.adminSvc.ApproveUser(userID); err != nil {
c.messageSvc.NotifyUserApproved(adminID.(uint), userID) ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=审核失败")
return
}
if err := c.messageSvc.NotifyUserApproved(adminID.(uint), userID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=审核通过但通知失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=用户已通过审核") ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=用户已通过审核")
} else if action == "reject" { } else if action == "reject" {
c.adminSvc.RejectUser(userID) if err := c.adminSvc.RejectUser(userID); err != nil {
c.messageSvc.NotifyUserRejected(adminID.(uint), userID) ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=拒绝失败")
return
}
if err := c.messageSvc.NotifyUserRejected(adminID.(uint), userID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=拒绝成功但通知失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=用户已拒绝") ctx.Redirect(http.StatusFound, "/admin/pending_users?msg=用户已拒绝")
} else { } else {
ctx.Redirect(http.StatusFound, "/admin/pending_users") ctx.Redirect(http.StatusFound, "/admin/pending_users")
@@ -104,7 +123,10 @@ func (c *AdminController) Posts(ctx *gin.Context) {
func (c *AdminController) DeletePost(ctx *gin.Context) { func (c *AdminController) DeletePost(ctx *gin.Context) {
postID := parseUint(ctx.Param("id")) postID := parseUint(ctx.Param("id"))
c.adminSvc.DeletePost(postID) if err := c.adminSvc.DeletePost(postID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/posts?msg=删除失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/posts?msg=帖子已删除") ctx.Redirect(http.StatusFound, "/admin/posts?msg=帖子已删除")
} }
@@ -134,10 +156,16 @@ func (c *AdminController) UpdateUserRole(ctx *gin.Context) {
return return
} }
c.adminSvc.UpdateUserRole(uint(userID), newRole) if err := c.adminSvc.UpdateUserRole(uint(userID), newRole); err != nil {
ctx.Redirect(http.StatusFound, "/admin/users?msg=更新失败")
return
}
if newRole == "banned" { if newRole == "banned" {
c.messageSvc.NotifyUserBanned(currentUserID.(uint), uint(userID)) if err := c.messageSvc.NotifyUserBanned(currentUserID.(uint), uint(userID)); err != nil {
ctx.Redirect(http.StatusFound, "/admin/users?msg=封禁成功但通知失败")
return
}
} }
ctx.Redirect(http.StatusFound, "/admin/users?msg=用户角色已更新") ctx.Redirect(http.StatusFound, "/admin/users?msg=用户角色已更新")
@@ -152,7 +180,10 @@ func (c *AdminController) DeleteUser(ctx *gin.Context) {
return return
} }
c.adminSvc.DeleteUser(userID) if err := c.adminSvc.DeleteUser(userID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/users?msg=删除失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/users?msg=用户已删除") ctx.Redirect(http.StatusFound, "/admin/users?msg=用户已删除")
} }
@@ -170,7 +201,10 @@ func (c *AdminController) Comments(ctx *gin.Context) {
func (c *AdminController) DeleteComment(ctx *gin.Context) { func (c *AdminController) DeleteComment(ctx *gin.Context) {
commentID := parseUint(ctx.Param("id")) commentID := parseUint(ctx.Param("id"))
c.adminSvc.DeleteComment(commentID) if err := c.adminSvc.DeleteComment(commentID); err != nil {
ctx.Redirect(http.StatusFound, "/admin/comments?msg=删除失败")
return
}
ctx.Redirect(http.StatusFound, "/admin/comments?msg=评论已删除") ctx.Redirect(http.StatusFound, "/admin/comments?msg=评论已删除")
} }

View File

@@ -3,9 +3,10 @@ package controllers
import ( import (
"net/http" "net/http"
"lv8girl/internal/services"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"lv8girl/internal/services"
) )
type AuthController struct { type AuthController struct {
@@ -51,7 +52,10 @@ func (c *AuthController) Login(ctx *gin.Context) {
session.Set("user_id", result.User.ID) session.Set("user_id", result.User.ID)
session.Set("username", result.User.Username) session.Set("username", result.User.Username)
session.Set("user_role", result.User.Role) session.Set("user_role", result.User.Role)
session.Save() if err := session.Save(); err != nil {
ctx.HTML(http.StatusOK, "login.html", gin.H{"Error": "登录失败,请重试"})
return
}
ctx.Redirect(http.StatusFound, "/") ctx.Redirect(http.StatusFound, "/")
} }
@@ -94,6 +98,9 @@ func (c *AuthController) Register(ctx *gin.Context) {
func (c *AuthController) Logout(ctx *gin.Context) { func (c *AuthController) Logout(ctx *gin.Context) {
session := sessions.Default(ctx) session := sessions.Default(ctx)
session.Clear() session.Clear()
session.Save() if err := session.Save(); err != nil {
ctx.String(http.StatusInternalServerError, "退出失败")
return
}
ctx.Redirect(http.StatusFound, "/") ctx.Redirect(http.StatusFound, "/")
} }

View File

@@ -7,9 +7,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/gin-gonic/gin"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"lv8girl/internal/services" "lv8girl/internal/services"
"github.com/gin-gonic/gin"
) )
type DiscussionController struct { type DiscussionController struct {
@@ -72,7 +73,17 @@ func (c *DiscussionController) CreatePost(ctx *gin.Context) {
uploadDir := "uploads/posts" uploadDir := "uploads/posts"
if _, err := os.Stat(uploadDir); os.IsNotExist(err) { if _, err := os.Stat(uploadDir); os.IsNotExist(err) {
os.MkdirAll(uploadDir, 0755) if err := os.MkdirAll(uploadDir, 0755); err != nil {
ctx.HTML(http.StatusOK, "post_discussion.html", gin.H{
"IsLoggedIn": true,
"UserID": userID,
"Username": username,
"UserRole": userRole,
"Error": "创建上传目录失败",
"Success": "",
})
return
}
} }
imagePath = filepath.Join(uploadDir, filename) imagePath = filepath.Join(uploadDir, filename)

View File

@@ -4,11 +4,12 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"lv8girl/internal/repositories" "lv8girl/internal/repositories"
"lv8girl/internal/services" "lv8girl/internal/services"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
) )
type HomeController struct { type HomeController struct {
@@ -36,7 +37,7 @@ func (c *HomeController) Index(ctx *gin.Context) {
if isLoggedIn { if isLoggedIn {
userRepo := repositories.NewUserRepository() userRepo := repositories.NewUserRepository()
userRepo.UpdateLastActive(userID) _ = userRepo.UpdateLastActive(userID)
} }
posts, _ := c.discussionSvc.GetApprovedPosts(30) posts, _ := c.discussionSvc.GetApprovedPosts(30)
@@ -86,10 +87,10 @@ func (c *HomeController) ShowPost(ctx *gin.Context) {
} }
if !viewedMap[postID] { if !viewedMap[postID] {
c.discussionSvc.IncrementViews(postID) _ = c.discussionSvc.IncrementViews(postID)
viewedMap[postID] = true viewedMap[postID] = true
session.Set(viewedKey, viewedMap) session.Set(viewedKey, viewedMap)
session.Save() _ = session.Save()
} }
detail, err := c.discussionSvc.GetPostDetail(postID, userID) detail, err := c.discussionSvc.GetPostDetail(postID, userID)
@@ -129,7 +130,7 @@ func (c *HomeController) LikePost(ctx *gin.Context) {
} }
postID := parseUint(ctx.Param("id")) postID := parseUint(ctx.Param("id"))
c.discussionSvc.AddLike(postID, userID) _ = c.discussionSvc.AddLike(postID, userID)
ctx.Redirect(http.StatusFound, "/post/"+ctx.Param("id")) ctx.Redirect(http.StatusFound, "/post/"+ctx.Param("id"))
} }
@@ -144,7 +145,7 @@ func (c *HomeController) AddComment(ctx *gin.Context) {
content := ctx.PostForm("content") content := ctx.PostForm("content")
if content != "" { if content != "" {
c.discussionSvc.AddComment(postID, userID, content) _ = c.discussionSvc.AddComment(postID, userID, content)
} }
ctx.Redirect(http.StatusFound, "/post/"+ctx.Param("id")) ctx.Redirect(http.StatusFound, "/post/"+ctx.Param("id"))

View File

@@ -4,10 +4,11 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/gin-gonic/gin"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"lv8girl/internal/repositories" "lv8girl/internal/repositories"
"lv8girl/internal/services" "lv8girl/internal/services"
"github.com/gin-gonic/gin"
) )
type MessageController struct { type MessageController struct {
@@ -79,7 +80,10 @@ func (c *MessageController) SendMessage(ctx *gin.Context) {
return return
} }
c.messageSvc.SendMessage(currentUserID, toUserID, content) err := c.messageSvc.SendMessage(currentUserID, toUserID, content)
if err != nil {
return
}
ctx.HTML(http.StatusOK, "send_message.html", gin.H{ ctx.HTML(http.StatusOK, "send_message.html", gin.H{
"Receiver": receiver, "Receiver": receiver,

View File

@@ -8,9 +8,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/gin-gonic/gin"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"lv8girl/internal/services" "lv8girl/internal/services"
"github.com/gin-gonic/gin"
) )
type UserController struct { type UserController struct {
@@ -86,7 +87,10 @@ func (c *UserController) UploadAvatar(ctx *gin.Context) {
uploadDir := "uploads/avatars" uploadDir := "uploads/avatars"
if _, err := os.Stat(uploadDir); os.IsNotExist(err) { if _, err := os.Stat(uploadDir); os.IsNotExist(err) {
os.MkdirAll(uploadDir, 0755) if err := os.MkdirAll(uploadDir, 0755); err != nil {
ctx.Redirect(http.StatusFound, "/profile?error=创建上传目录失败")
return
}
} }
imagePath := filepath.Join(uploadDir, filename) imagePath := filepath.Join(uploadDir, filename)
@@ -97,6 +101,9 @@ func (c *UserController) UploadAvatar(ctx *gin.Context) {
// 将路径转换为 URL 友好的格式(使用正斜杠) // 将路径转换为 URL 友好的格式(使用正斜杠)
avatarPath := filepath.ToSlash(imagePath) avatarPath := filepath.ToSlash(imagePath)
c.userSvc.UpdateAvatar(userID, avatarPath) if err := c.userSvc.UpdateAvatar(userID, avatarPath); err != nil {
ctx.Redirect(http.StatusFound, "/profile?error=头像更新失败")
return
}
ctx.Redirect(http.StatusFound, "/profile?success=头像更新成功") ctx.Redirect(http.StatusFound, "/profile?success=头像更新成功")
} }

View File

@@ -7,6 +7,7 @@ import (
) )
type User struct { type User struct {
//goland:noinspection SpellCheckingInspection
ID uint `gorm:"primaryKey" json:"id"` ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"size:50;uniqueIndex;not null" json:"username"` Username string `gorm:"size:50;uniqueIndex;not null" json:"username"`
Email string `gorm:"size:100;uniqueIndex;not null" json:"email"` Email string `gorm:"size:100;uniqueIndex;not null" json:"email"`

View File

@@ -3,8 +3,9 @@ package repositories
import ( import (
"time" "time"
"gorm.io/gorm"
"lv8girl/internal/models" "lv8girl/internal/models"
"gorm.io/gorm"
) )
type DiscussionRepository struct{} type DiscussionRepository struct{}

View File

@@ -3,8 +3,9 @@ package repositories
import ( import (
"time" "time"
"golang.org/x/crypto/bcrypt"
"lv8girl/internal/models" "lv8girl/internal/models"
"golang.org/x/crypto/bcrypt"
) )
type UserRepository struct{} type UserRepository struct{}

View File

@@ -1,12 +1,13 @@
package routes package routes
import ( import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"lv8girl/internal/config" "lv8girl/internal/config"
"lv8girl/internal/controllers" "lv8girl/internal/controllers"
"lv8girl/internal/middleware" "lv8girl/internal/middleware"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
) )
func SetupRouter() *gin.Engine { func SetupRouter() *gin.Engine {

View File

@@ -44,7 +44,7 @@ func (s *AuthService) Login(login, password string) LoginResult {
return LoginResult{Success: false, Error: "账号状态异常,请联系管理员"} return LoginResult{Success: false, Error: "账号状态异常,请联系管理员"}
} }
s.userRepo.UpdateLastActive(user.ID) _ = s.userRepo.UpdateLastActive(user.ID)
return LoginResult{Success: true, User: user} return LoginResult{Success: true, User: user}
} }

View File

@@ -1,9 +0,0 @@
package utils
func Substring(s string, length int) string {
runes := []rune(s)
if len(runes) <= length {
return s
}
return string(runes[:length])
}

View File

@@ -40,18 +40,6 @@
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; } .stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.stat-number { font-size: 2.5rem; font-weight: 700; color: var(--primary); line-height: 1.2; margin-bottom: 4px; } .stat-number { font-size: 2.5rem; font-weight: 700; color: var(--primary); line-height: 1.2; margin-bottom: 4px; }
.stat-label { color: var(--text-hint); font-size: 0.95rem; } .stat-label { color: var(--text-hint); font-size: 0.95rem; }
.table-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
table { width: 100%; border-collapse: collapse; }
th { background: var(--surface-light); padding: 12px 15px; text-align: left; font-weight: 600; color: var(--primary); border-bottom: 1px solid var(--border); }
td { padding: 12px 15px; border-bottom: 1px solid var(--border); color: var(--text-soft); }
tr:last-child td { border-bottom: none; }
tr:hover { background: var(--surface-light); }
.actions a { margin-right: 10px; color: var(--text-hint); text-decoration: none; }
.actions a:hover { color: var(--primary); }
.actions .delete { color: #ff6b6b; }
.actions .delete:hover { color: #ff4d4d; }
.actions .approve { color: var(--primary); }
.actions .approve:hover { color: var(--primary-light); }
.message { background: var(--surface-light); border-left: 4px solid var(--primary); padding: 12px 20px; margin-bottom: 20px; border-radius: 8px; color: var(--text); } .message { background: var(--surface-light); border-left: 4px solid var(--primary); padding: 12px 20px; margin-bottom: 20px; border-radius: 8px; color: var(--text); }
@media (max-width: 768px) { .admin-wrapper { flex-direction: column; } .sidebar { width: 100%; height: auto; position: static; } } @media (max-width: 768px) { .admin-wrapper { flex-direction: column; } .sidebar { width: 100%; height: auto; position: static; } }
</style> </style>
@@ -64,7 +52,7 @@
<p>管理面板</p> <p>管理面板</p>
</div> </div>
<ul class="sidebar-menu"> <ul class="sidebar-menu">
<li><a href="/admin" class="{{if eq .Page "dashboard"}}active{{end}}">📊 仪表盘</a></li> <li><a href="/admin" class="active">📊 仪表盘</a></li>
<li><a href="/admin/pending_posts" class="{{if eq .Page "pending_posts"}}active{{end}}">⏳ 待审核帖子</a></li> <li><a href="/admin/pending_posts" class="{{if eq .Page "pending_posts"}}active{{end}}">⏳ 待审核帖子</a></li>
<li><a href="/admin/pending_users" class="{{if eq .Page "pending_users"}}active{{end}}">👥 待审核用户</a></li> <li><a href="/admin/pending_users" class="{{if eq .Page "pending_users"}}active{{end}}">👥 待审核用户</a></li>
<li><a href="/admin/posts" class="{{if eq .Page "posts"}}active{{end}}">📝 帖子管理</a></li> <li><a href="/admin/posts" class="{{if eq .Page "posts"}}active{{end}}">📝 帖子管理</a></li>

View File

@@ -35,6 +35,7 @@
tr:hover { background: var(--surface-light); } tr:hover { background: var(--surface-light); }
.actions a { margin-right: 10px; color: var(--text-hint); text-decoration: none; } .actions a { margin-right: 10px; color: var(--text-hint); text-decoration: none; }
.actions .delete { color: #ff6b6b; } .actions .delete { color: #ff6b6b; }
/* 状态样式通过模板动态应用: status-{{.Status}} */
.status-approved { color: var(--primary); } .status-approved { color: var(--primary); }
.status-pending { color: #ffb347; } .status-pending { color: #ffb347; }
.status-rejected { color: #ff6b6b; } .status-rejected { color: #ff6b6b; }
@@ -42,6 +43,8 @@
</style> </style>
</head> </head>
<body> <body>
<!-- 状态样式类通过模板动态使用: status-approved, status-pending, status-rejected -->
<span style="display:none;" class="status-approved status-pending status-rejected"></span>
<div class="admin-wrapper"> <div class="admin-wrapper">
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-header"><div class="logo">lv8girl</div><p>管理面板</p></div> <div class="sidebar-header"><div class="logo">lv8girl</div><p>管理面板</p></div>

View File

@@ -43,6 +43,8 @@
</style> </style>
</head> </head>
<body> <body>
<!-- 状态样式类通过模板动态使用: status-approved, status-pending, status-rejected -->
<span style="display:none;" class="status-approved status-pending status-rejected"></span>
<div class="admin-wrapper"> <div class="admin-wrapper">
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-header"><div class="logo">lv8girl</div><p>管理面板</p></div> <div class="sidebar-header"><div class="logo">lv8girl</div><p>管理面板</p></div>
@@ -78,7 +80,8 @@
{{else}} {{else}}
<form method="post" action="/admin/users/role" style="display:inline;"> <form method="post" action="/admin/users/role" style="display:inline;">
<input type="hidden" name="user_id" value="{{.ID}}"> <input type="hidden" name="user_id" value="{{.ID}}">
<select name="new_role" onchange="this.form.submit()"> <label for="role-{{.ID}}" style="display:none;">角色</label>
<select id="role-{{.ID}}" name="new_role" onchange="this.form.submit()">
<option value="user" {{if eq .Role "user"}}selected{{end}}>用户</option> <option value="user" {{if eq .Role "user"}}selected{{end}}>用户</option>
<option value="admin" {{if eq .Role "admin"}}selected{{end}}>管理员</option> <option value="admin" {{if eq .Role "admin"}}selected{{end}}>管理员</option>
<option value="banned" {{if eq .Role "banned"}}selected{{end}}>封禁</option> <option value="banned" {{if eq .Role "banned"}}selected{{end}}>封禁</option>

View File

@@ -97,7 +97,7 @@
} }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div class="top-bar"> <div class="top-bar">

View File

@@ -50,7 +50,7 @@
.footer-text a:hover { text-decoration: underline; } .footer-text a:hover { text-decoration: underline; }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="login-wrapper"> <div class="login-wrapper">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -61,12 +61,12 @@
{{if .Error}}<div class="error-message">{{.Error}}</div>{{end}} {{if .Error}}<div class="error-message">{{.Error}}</div>{{end}}
<form method="post"> <form method="post">
<div class="form-group"> <div class="form-group">
<label>用户名 / 邮箱</label> <label for="login">用户名 / 邮箱</label>
<input type="text" name="login" placeholder="请输入用户名或邮箱" required> <input type="text" id="login" name="login" placeholder="请输入用户名或邮箱" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>密码</label> <label for="password">密码</label>
<input type="password" name="password" placeholder="请输入密码" required> <input type="password" id="password" name="password" placeholder="请输入密码" required>
</div> </div>
<button type="submit" class="btn">登 录</button> <button type="submit" class="btn">登 录</button>
</form> </form>

View File

@@ -57,7 +57,7 @@
.footer a:hover { color: var(--accent); } .footer a:hover { color: var(--accent); }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>

View File

@@ -104,7 +104,9 @@
@media (max-width: 800px) { .main-layout { flex-direction: column; } } @media (max-width: 800px) { .main-layout { flex-direction: column; } }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<!-- liked 类通过模板动态应用 -->
<span style="display:none;" class="like-btn liked"></span>
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -160,7 +162,8 @@
<h2 class="comments-title">评论 ({{len .Comments}})</h2> <h2 class="comments-title">评论 ({{len .Comments}})</h2>
{{if .IsLoggedIn}} {{if .IsLoggedIn}}
<form method="post" action="/post/{{.Post.ID}}/comment" class="comment-form"> <form method="post" action="/post/{{.Post.ID}}/comment" class="comment-form">
<textarea name="content" placeholder="写下你的评论..." required></textarea> <label for="comment-content" style="display:none;">评论内容</label>
<textarea id="comment-content" name="content" placeholder="写下你的评论..." required></textarea>
<button type="submit">发表评论</button> <button type="submit">发表评论</button>
</form> </form>
{{else}} {{else}}

View File

@@ -54,7 +54,7 @@
.footer-links a:hover { color: var(--accent); } .footer-links a:hover { color: var(--accent); }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="post-wrapper"> <div class="post-wrapper">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -68,16 +68,16 @@
{{else}} {{else}}
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
<div class="form-group"> <div class="form-group">
<label>标题</label> <label for="title">标题</label>
<input type="text" name="title" placeholder="请输入标题" required> <input type="text" id="title" name="title" placeholder="请输入标题" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>内容</label> <label for="content">内容</label>
<textarea name="content" placeholder="请输入帖子内容..." required></textarea> <textarea id="content" name="content" placeholder="请输入帖子内容..." required></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>上传图片可选不超过2MB</label> <label for="image">上传图片可选不超过2MB</label>
<input type="file" name="image" accept="image/*"> <input type="file" id="image" name="image" accept="image/*">
<div class="file-note">支持 JPEG、PNG、GIF、WEBP 格式</div> <div class="file-note">支持 JPEG、PNG、GIF、WEBP 格式</div>
</div> </div>
<button type="submit" class="btn">发 布</button> <button type="submit" class="btn">发 布</button>

View File

@@ -46,9 +46,6 @@
.dropdown a:hover { background: var(--surface-light); } .dropdown a:hover { background: var(--surface-light); }
.theme-toggle { background: var(--surface-light); border: none; color: var(--text); font-size: 1.3rem; width: 38px; height: 38px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s; } .theme-toggle { background: var(--surface-light); border: none; color: var(--text); font-size: 1.3rem; width: 38px; height: 38px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: 0.2s; }
.theme-toggle:hover { background: var(--accent); color: var(--surface); } .theme-toggle:hover { background: var(--accent); color: var(--surface); }
.success-message, .error-message { padding: 12px 20px; border-radius: 8px; margin-bottom: 20px; }
.success-message { background: var(--primary); color: white; }
.error-message { background: #ff6b6b; color: white; }
.profile-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 25px; margin-bottom: 30px; display: flex; gap: 25px; flex-wrap: wrap; } .profile-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 25px; margin-bottom: 30px; display: flex; gap: 25px; flex-wrap: wrap; }
.profile-avatar { width: 100px; height: 100px; border-radius: 50%; background: var(--gradient); overflow: hidden; display: flex; align-items: center; justify-content: center; color: white; font-size: 2.5rem; font-weight: 600; flex-shrink: 0; } .profile-avatar { width: 100px; height: 100px; border-radius: 50%; background: var(--gradient); overflow: hidden; display: flex; align-items: center; justify-content: center; color: white; font-size: 2.5rem; font-weight: 600; flex-shrink: 0; }
.profile-avatar img { width: 100%; height: 100%; object-fit: cover; } .profile-avatar img { width: 100%; height: 100%; object-fit: cover; }
@@ -85,7 +82,7 @@
.footer a:hover { color: var(--accent); } .footer a:hover { color: var(--accent); }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -135,6 +132,7 @@
</form> </form>
</div> </div>
{{else}} {{else}}
<!--suppress HtmlUnknownTarget-->
<a href="/send-message?to={{.User.ID}}" class="edit-btn" style="margin-top:10px;">📩 发送私信</a> <a href="/send-message?to={{.User.ID}}" class="edit-btn" style="margin-top:10px;">📩 发送私信</a>
{{end}} {{end}}
</div> </div>
@@ -187,9 +185,12 @@
themeToggle.textContent = document.body.classList.contains('dark-mode') ? '☀️' : '🌓'; themeToggle.textContent = document.body.classList.contains('dark-mode') ? '☀️' : '🌓';
}); });
function toggleUpload() { function toggleUpload() {
var form = document.getElementById('uploadForm'); /** @type {HTMLElement|null} */
const form = document.getElementById('uploadForm');
if (form instanceof HTMLElement) {
form.style.display = form.style.display === 'none' ? 'block' : 'none'; form.style.display = form.style.display === 'none' ? 'block' : 'none';
} }
}
</script> </script>
</body> </body>
</html> </html>

View File

@@ -55,7 +55,7 @@
.footer-text a:hover { text-decoration: underline; } .footer-text a:hover { text-decoration: underline; }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="register-wrapper"> <div class="register-wrapper">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -69,20 +69,20 @@
{{else}} {{else}}
<form method="post"> <form method="post">
<div class="form-group"> <div class="form-group">
<label>用户名</label> <label for="username">用户名</label>
<input type="text" name="username" placeholder="3-20个字符支持中文、字母、数字、下划线" required> <input type="text" id="username" name="username" placeholder="3-20个字符支持中文、字母、数字、下划线" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>邮箱</label> <label for="email">邮箱</label>
<input type="email" name="email" placeholder="请输入有效邮箱" required> <input type="email" id="email" name="email" placeholder="请输入有效邮箱" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>密码</label> <label for="password">密码</label>
<input type="password" name="password" placeholder="至少6位" required> <input type="password" id="password" name="password" placeholder="至少6位" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>确认密码</label> <label for="confirm_password">确认密码</label>
<input type="password" name="confirm_password" placeholder="请再次输入密码" required> <input type="password" id="confirm_password" name="confirm_password" placeholder="请再次输入密码" required>
</div> </div>
<div class="checkbox-group"> <div class="checkbox-group">
<input type="checkbox" name="agree" id="agree" required> <input type="checkbox" name="agree" id="agree" required>

View File

@@ -50,7 +50,7 @@
.footer-links a:hover { color: var(--accent); } .footer-links a:hover { color: var(--accent); }
</style> </style>
</head> </head>
<body> <body class="dark-mode">
<div class="wrapper"> <div class="wrapper">
<div class="header"> <div class="header">
<div class="logo">lv8girl<span>绿坝娘</span></div> <div class="logo">lv8girl<span>绿坝娘</span></div>
@@ -62,8 +62,8 @@
{{if .Success}}<div class="success-message">{{.Success}}</div>{{end}} {{if .Success}}<div class="success-message">{{.Success}}</div>{{end}}
<form method="post"> <form method="post">
<div class="form-group"> <div class="form-group">
<label>内容</label> <label for="content">内容</label>
<textarea name="content" placeholder="请输入私信内容..."></textarea> <textarea id="content" name="content" placeholder="请输入私信内容..."></textarea>
</div> </div>
<button type="submit" class="btn">发送</button> <button type="submit" class="btn">发送</button>
</form> </form>