Compare commits
	
		
			3 Commits
		
	
	
		
			a7ef859c43
			...
			d4044b0eaf
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d4044b0eaf | |||
| a84156f0a0 | |||
| 001e3b66cb | 
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.mod
									
									
									
									
									
								
							@@ -29,19 +29,20 @@ require (
 | 
			
		||||
	github.com/jinzhu/now v1.1.5 // indirect
 | 
			
		||||
	github.com/json-iterator/go v1.1.12 // indirect
 | 
			
		||||
	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
 | 
			
		||||
	github.com/kr/text v0.2.0 // indirect
 | 
			
		||||
	github.com/kr/pretty v0.3.1 // indirect
 | 
			
		||||
	github.com/leodido/go-urn v1.4.0 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.20 // indirect
 | 
			
		||||
	github.com/mattn/go-sqlite3 v1.14.29 // indirect
 | 
			
		||||
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 | 
			
		||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
			
		||||
	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
 | 
			
		||||
	github.com/rogpeppe/go-internal v1.12.0 // indirect
 | 
			
		||||
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 | 
			
		||||
	github.com/ugorji/go/codec v1.3.0 // indirect
 | 
			
		||||
	golang.org/x/arch v0.19.0 // indirect
 | 
			
		||||
	golang.org/x/arch v0.21.0 // indirect
 | 
			
		||||
	golang.org/x/crypto v0.40.0 // indirect
 | 
			
		||||
	golang.org/x/net v0.42.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.34.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.36.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.27.0 // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.36.6 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										57
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								go.sum
									
									
									
									
									
								
							@@ -1,12 +1,8 @@
 | 
			
		||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 | 
			
		||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 | 
			
		||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
 | 
			
		||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 | 
			
		||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
 | 
			
		||||
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
 | 
			
		||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
 | 
			
		||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
 | 
			
		||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
 | 
			
		||||
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
 | 
			
		||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
 | 
			
		||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
 | 
			
		||||
@@ -18,16 +14,12 @@ 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/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
 | 
			
		||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
 | 
			
		||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
 | 
			
		||||
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
 | 
			
		||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
 | 
			
		||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
 | 
			
		||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
 | 
			
		||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
 | 
			
		||||
github.com/gin-contrib/static v1.1.5 h1:bAPqT4KTZN+4uDY1b90eSrD1t8iNzod7Jj8njwmnzz4=
 | 
			
		||||
github.com/gin-contrib/static v1.1.5/go.mod h1:8JSEXwZHcQ0uCrLPcsvnAJ4g+ODxeupP8Zetl9fd8wM=
 | 
			
		||||
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.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
 | 
			
		||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 | 
			
		||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 | 
			
		||||
@@ -36,23 +28,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 | 
			
		||||
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/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 | 
			
		||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
 | 
			
		||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 | 
			
		||||
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
 | 
			
		||||
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
 | 
			
		||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
 | 
			
		||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
 | 
			
		||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 | 
			
		||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
 | 
			
		||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 | 
			
		||||
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
 | 
			
		||||
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 | 
			
		||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
 | 
			
		||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 | 
			
		||||
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/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 | 
			
		||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 | 
			
		||||
@@ -63,21 +48,17 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
 | 
			
		||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 | 
			
		||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
 | 
			
		||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 | 
			
		||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 | 
			
		||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 | 
			
		||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 | 
			
		||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 | 
			
		||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
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/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/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.29 h1:1O6nRLJKvsi1H2Sj0Hzdfojwt8GiGKm+LOfLaBFaouQ=
 | 
			
		||||
github.com/mattn/go-sqlite3 v1.14.29/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
@@ -87,10 +68,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 | 
			
		||||
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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 | 
			
		||||
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/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 | 
			
		||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 | 
			
		||||
@@ -103,33 +86,19 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
 | 
			
		||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
			
		||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 | 
			
		||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 | 
			
		||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
 | 
			
		||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 | 
			
		||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
 | 
			
		||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
 | 
			
		||||
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
 | 
			
		||||
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
 | 
			
		||||
golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU=
 | 
			
		||||
golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
 | 
			
		||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
 | 
			
		||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
 | 
			
		||||
golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw=
 | 
			
		||||
golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
 | 
			
		||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
 | 
			
		||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 | 
			
		||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
 | 
			
		||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
 | 
			
		||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
 | 
			
		||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 | 
			
		||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
 | 
			
		||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
			
		||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
 | 
			
		||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
			
		||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
 | 
			
		||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
 | 
			
		||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
 | 
			
		||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 | 
			
		||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
 | 
			
		||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
 | 
			
		||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
@@ -140,12 +109,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
 | 
			
		||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
 | 
			
		||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
 | 
			
		||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
 | 
			
		||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
 | 
			
		||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
 | 
			
		||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
 | 
			
		||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
 | 
			
		||||
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
 | 
			
		||||
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
 | 
			
		||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										115
									
								
								internal/auth/oauth2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								internal/auth/oauth2.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
package auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"CatsOfMastodonBotGo/internal/config"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type GiteaOAuth2Handler struct {
 | 
			
		||||
	ClientID     string
 | 
			
		||||
	ClientSecret string
 | 
			
		||||
	InstanceUrl  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var GiteaOauth2HandlerInstance *GiteaOAuth2Handler
 | 
			
		||||
 | 
			
		||||
func InitGiteaOauth2Token() {
 | 
			
		||||
	GiteaOauth2HandlerInstance = &GiteaOAuth2Handler{
 | 
			
		||||
		ClientID:     config.Config.GiteaOauthClientID,
 | 
			
		||||
		ClientSecret: config.Config.GiteaOauthClientSecret,
 | 
			
		||||
		InstanceUrl:  config.Config.GiteaOauthInstance,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *GiteaOAuth2Handler) GetGiteaLoginURL(redirectHost string) (string, error) {
 | 
			
		||||
	if g.InstanceUrl == "" {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	if redirectHost == "" {
 | 
			
		||||
		slog.Error("Redirect host not provided")
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	authUrl := g.InstanceUrl + "/login/oauth/authorize?client_id=" + g.ClientID + "&redirect_uri=" + "http://" + redirectHost + "/admin/oauth/gitea/callback&scope=openid&response_type=code&response_mode=form_post"
 | 
			
		||||
	return authUrl, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *GiteaOAuth2Handler) GetGiteaUserEmailByCode(code string) (string, error) {
 | 
			
		||||
 | 
			
		||||
	if g.InstanceUrl == "" {
 | 
			
		||||
        slog.Error("Instance URL not provided")
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	// No need to verify since we are accesing the gitea once and only for the email
 | 
			
		||||
	accessToken, err := g.getGiteaAccessTokenByCode(code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userInfoUrl := g.InstanceUrl + "/login/oauth/userinfo"
 | 
			
		||||
	slog.Info(userInfoUrl)
 | 
			
		||||
	req, err := http.NewRequest("POST", userInfoUrl, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Authorization", "Bearer "+accessToken)
 | 
			
		||||
	req.Header.Set("Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
	resp, err := http.DefaultClient.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var userInfo struct {
 | 
			
		||||
		Email string `json:"email"`
 | 
			
		||||
	}
 | 
			
		||||
    
 | 
			
		||||
	if err := json.Unmarshal(body, &userInfo); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return userInfo.Email, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *GiteaOAuth2Handler) getGiteaAccessTokenByCode(code string) (string, error) {
 | 
			
		||||
	form := url.Values{}
 | 
			
		||||
	form.Add("client_id", g.ClientID)
 | 
			
		||||
	form.Add("client_secret", g.ClientSecret)
 | 
			
		||||
	form.Add("code", code)
 | 
			
		||||
	form.Add("grant_type", "authorization_code")
 | 
			
		||||
 | 
			
		||||
	req, err := http.NewRequest("POST", g.InstanceUrl+"/login/oauth/access_token", bytes.NewBufferString(form.Encode()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 | 
			
		||||
	req.Header.Set("Accept", "application/json")
 | 
			
		||||
 | 
			
		||||
	resp, err := http.DefaultClient.Do(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, _ := io.ReadAll(resp.Body)
 | 
			
		||||
	var tokenResp struct {
 | 
			
		||||
		AccessToken string `json:"access_token"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := json.Unmarshal(body, &tokenResp); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return tokenResp.AccessToken, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ package config
 | 
			
		||||
import (
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/joho/godotenv"
 | 
			
		||||
)
 | 
			
		||||
@@ -24,6 +25,11 @@ type config struct {
 | 
			
		||||
	DBName     string
 | 
			
		||||
 | 
			
		||||
	ImageKitId string
 | 
			
		||||
 | 
			
		||||
	GiteaOauthInstance	string
 | 
			
		||||
	GiteaOauthClientID	string
 | 
			
		||||
	GiteaOauthClientSecret	string
 | 
			
		||||
	GiteaOauthAllowedEmails	[]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var Config *config
 | 
			
		||||
@@ -74,6 +80,16 @@ func Load() *config {
 | 
			
		||||
	dbPassword := os.Getenv("CAOM_DB_PASSWORD")
 | 
			
		||||
	dbName := os.Getenv("CAOM_DB_NAME")
 | 
			
		||||
 | 
			
		||||
	giteaOauthInstance := os.Getenv("CAOM_GITEA_OAUTH_INSTANCE")
 | 
			
		||||
	giteaOauthClientID := os.Getenv("CAOM_GITEA_OAUTH_CLIENT_ID")
 | 
			
		||||
	giteaOauthClientSecret := os.Getenv("CAOM_GITEA_OAUTH_CLIENT_SECRET")
 | 
			
		||||
 | 
			
		||||
	giteaOauthAllowedEmails := os.Getenv("CAOM_GITEA_OAUTH_ALLOWED_EMAILS")
 | 
			
		||||
	var giteaOauthAllowedEmailsParsed []string
 | 
			
		||||
	if giteaOauthAllowedEmails != "" {
 | 
			
		||||
		giteaOauthAllowedEmailsParsed = strings.Split(giteaOauthAllowedEmails, ",")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dbEngine == "" || dbHost == "" || dbPort == "" || dbUser == "" || dbPassword == "" || dbName == "" {
 | 
			
		||||
		slog.Info("No database connection provided, using sqlite")
 | 
			
		||||
		dbEngine = "sqlite"
 | 
			
		||||
@@ -106,6 +122,11 @@ func Load() *config {
 | 
			
		||||
		DBName:     dbName,
 | 
			
		||||
 | 
			
		||||
		ImageKitId: imageKitId,
 | 
			
		||||
 | 
			
		||||
		GiteaOauthInstance:	giteaOauthInstance,
 | 
			
		||||
		GiteaOauthClientID:	giteaOauthClientID,
 | 
			
		||||
		GiteaOauthClientSecret:	giteaOauthClientSecret,
 | 
			
		||||
		GiteaOauthAllowedEmails: giteaOauthAllowedEmailsParsed,
 | 
			
		||||
	}
 | 
			
		||||
	return appContext
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,11 @@ func SetupRouter() *gin.Engine {
 | 
			
		||||
	r.LoadHTMLGlob("internal/web/templates/home/*")
 | 
			
		||||
 | 
			
		||||
	auth.InitJwtTokenGenerator() // Must be befor initializing admin handler, otherwise 'panic: runtime error: invalid memory address or nil pointer dereference'
 | 
			
		||||
	auth.InitGiteaOauth2Token()
 | 
			
		||||
	handlers.InitAdminDashboardHandler()
 | 
			
		||||
	handlers.InitApiEndpointHandler()
 | 
			
		||||
	handlers.InitEmbedCardHandler()
 | 
			
		||||
	handlers.InitOauthLoginHandler()
 | 
			
		||||
 | 
			
		||||
	// Main page
 | 
			
		||||
	r.GET("/", func(c *gin.Context) {
 | 
			
		||||
@@ -44,9 +46,13 @@ func SetupRouter() *gin.Engine {
 | 
			
		||||
	// 	c.HTML(http.StatusOK, "admin/index.html", nil)
 | 
			
		||||
	// })
 | 
			
		||||
	r.Use(static.Serve("/admin", static.LocalFile("internal/web/templates/admin", true)))
 | 
			
		||||
	// I dont know a better way for path handling
 | 
			
		||||
	r.Use(static.Serve("/admin/oauth/gitea/callback", static.LocalFile("internal/web/templates/admin", true)))
 | 
			
		||||
 | 
			
		||||
	adminApi := admin.Group("/api")
 | 
			
		||||
	adminApi.POST("/login", handlers.AdminDashboardHandlerInstance.Login)
 | 
			
		||||
	adminApi.GET("/login/oauth/gitea", handlers.OauthLoginHandlerInstance.GoToGiteaLogin)
 | 
			
		||||
	adminApi.POST("/login/oauth/gitea/final", handlers.OauthLoginHandlerInstance.LoginWithGitea)
 | 
			
		||||
	adminApi.GET("/getmedia", auth.JwtTokenGeneratorInstance.GinMiddleware(), handlers.AdminDashboardHandlerInstance.GetMedia)
 | 
			
		||||
	adminApi.POST("/approve", auth.JwtTokenGeneratorInstance.GinMiddleware(), handlers.AdminDashboardHandlerInstance.ApproveMedia)
 | 
			
		||||
	adminApi.POST("/reject", auth.JwtTokenGeneratorInstance.GinMiddleware(), handlers.AdminDashboardHandlerInstance.RejectMedia)
 | 
			
		||||
 
 | 
			
		||||
@@ -11,3 +11,7 @@ type LoginInput struct {
 | 
			
		||||
type RejectMediaInput struct {
 | 
			
		||||
	MediaId string `json:"mediaId" binding:"required"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GiteaLoginInput struct {
 | 
			
		||||
	Code string `json:"code" binding:"required"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								internal/web/handlers/oauth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								internal/web/handlers/oauth.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
package handlers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"CatsOfMastodonBotGo/internal/auth"
 | 
			
		||||
	"CatsOfMastodonBotGo/internal/config"
 | 
			
		||||
	"CatsOfMastodonBotGo/internal/web/dto"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type OauthLoginHandler struct {
 | 
			
		||||
	Jwt               auth.JwtTokenGenerator
 | 
			
		||||
	OauthLoginHandler *auth.GiteaOAuth2Handler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var OauthLoginHandlerInstance *OauthLoginHandler
 | 
			
		||||
 | 
			
		||||
func InitOauthLoginHandler() {
 | 
			
		||||
	OauthLoginHandlerInstance = &OauthLoginHandler{
 | 
			
		||||
		Jwt:               *auth.JwtTokenGeneratorInstance,
 | 
			
		||||
		OauthLoginHandler: auth.GiteaOauth2HandlerInstance,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (olh *OauthLoginHandler) GoToGiteaLogin(c *gin.Context) {
 | 
			
		||||
	redirectURL, _ := olh.OauthLoginHandler.GetGiteaLoginURL(c.Request.URL.Scheme + c.Request.Host)
 | 
			
		||||
	if redirectURL != "" {
 | 
			
		||||
		c.Redirect(http.StatusFound, redirectURL)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get gitea login url"})
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (olh *OauthLoginHandler) LoginWithGitea(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	var input dto.GiteaLoginInput
 | 
			
		||||
	if err := c.ShouldBindJSON(&input); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userEmail, err := olh.OauthLoginHandler.GetGiteaUserEmailByCode(input.Code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, email := range config.Config.GiteaOauthAllowedEmails {
 | 
			
		||||
		if email == userEmail {
 | 
			
		||||
			token, err := olh.Jwt.GenerateToken(map[string]interface{}{"role": "admin"})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Token generation failed"})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			c.JSON(http.StatusOK, gin.H{"message": "Login successful", "token": token})
 | 
			
		||||
		} else {
 | 
			
		||||
			c.JSON(401, gin.H{
 | 
			
		||||
				"error": "oath login faied or yyour email does not have access",
 | 
			
		||||
			})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								internal/web/templates/admin/assets/index-BvVLAYUm.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								internal/web/templates/admin/assets/index-BvVLAYUm.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										118
									
								
								internal/web/templates/admin/assets/index-D2PXWyfl.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								internal/web/templates/admin/assets/index-D2PXWyfl.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -13,8 +13,8 @@
 | 
			
		||||
    <meta property="og:type" content="website" />
 | 
			
		||||
 | 
			
		||||
    <meta name="twitter:card" content="summary_large_image" />
 | 
			
		||||
    <script type="module" crossorigin src="/admin/assets/index-JxecVd-K.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/admin/assets/index-DShmOgsI.css">
 | 
			
		||||
    <script type="module" crossorigin src="/admin/assets/index-D2PXWyfl.js"></script>
 | 
			
		||||
    <link rel="stylesheet" crossorigin href="/admin/assets/index-BvVLAYUm.css">
 | 
			
		||||
  </head>
 | 
			
		||||
 | 
			
		||||
  <body>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user