diff --git a/go.mod b/go.mod index b6f390b..11c134b 100644 --- a/go.mod +++ b/go.mod @@ -11,17 +11,42 @@ require ( require ( github.com/BurntSushi/toml v1.2.1 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/client9/misspell v0.3.4 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.10.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/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/google/go-cmp v0.5.8 // indirect github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kisielk/errcheck v1.6.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mdempsky/unconvert v0.0.0-20230125054757-2661c2c99a9b // 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.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect - golang.org/x/mod v0.7.0 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.5.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.4.2 // indirect mvdan.cc/gofumpt v0.4.0 // indirect ) diff --git a/go.sum b/go.sum index d7c11f2..57d76e2 100644 --- a/go.sum +++ b/go.sum @@ -2,24 +2,55 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/bradfitz/latlong v0.0.0-20170410180902-f3db6d0dff40/go.mod h1:ZcXX9BndVQx6Q/JM6B8x7dLE9sl20S+TQsv4KO7tEQk= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cespare/xxhash v1.0.0 h1:naDmySfoNg0nKS62/ujM6e71ZgM2AoVdaqGwMG0w18A= github.com/cespare/xxhash v1.0.0/go.mod h1:fX/lfQBkSCDXZSUgv6jVIu/EVA3/JNseAX5asI4c4T4= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= 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/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.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.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonas-p/go-shp v0.1.1/go.mod h1:MRIhyxDQ6VVp0oYeD7yPGr5RSTNScUFKCDsI5DR7PtI= +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/kisielk/errcheck v1.6.1 h1:cErYo+J4SmEjdXZrVXGwLJCE2sB06s23LpkcyWNrT+s= github.com/kisielk/errcheck v1.6.1/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kortschak/utter v0.0.0-20180609113506-364ec7d7a8f4 h1:pQnj+PSlG2m3GzNDRqfPKLGFa4F+UrGZVHfyMUcGiSA= github.com/kortschak/utter v0.0.0-20180609113506-364ec7d7a8f4/go.mod h1:oDr41C7kH9wvAikWyFhr6UFr8R7nelpmCF5XR5rL7I8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -29,27 +60,59 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/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/mdempsky/unconvert v0.0.0-20230125054757-2661c2c99a9b h1:jdFI9paVi4E33U9TAExBpKPl1l5MnOn7VOLbb4Mvzzg= github.com/mdempsky/unconvert v0.0.0-20230125054757-2661c2c99a9b/go.mod h1:mOq/NVYz3H5h7Av88ia14HIMF/UdGXj9dp8P/+b566A= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +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.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tealeg/xlsx v1.0.3/go.mod h1:uxu5UY2ovkuRPWKQ8Q7JG0JbSivrISjdPzZQKeo74mA= github.com/tormoder/fit v0.15.0 h1:oW1dhvGqPIwBJdRJfWzW/jqYU705oBmLcJq4TJO7SqU= github.com/tormoder/fit v0.15.0/go.mod h1:J+m0+sz5qljhPaP34CgJz8uFD8Vzdsf96D3Hj99DMLQ= +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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= @@ -58,6 +121,7 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -66,6 +130,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -86,6 +152,10 @@ golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -98,6 +168,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -106,13 +178,22 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.4.1-0.20221208213631-3f74d914ae6d/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.4.2 h1:6qXr+R5w+ktL5UkwEbPp+fEvfyoMPche6GkOpGHZcLc= honnef.co/go/tools v0.4.2/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/database/models.go b/internal/database/models.go index 6383bbc..337ac3a 100644 --- a/internal/database/models.go +++ b/internal/database/models.go @@ -2,8 +2,7 @@ package database import ( - "database/sql" - "time" + "time" ) type Activity struct { diff --git a/internal/database/sqlite.go b/internal/database/sqlite.go index 028f85d..2d25aa5 100644 --- a/internal/database/sqlite.go +++ b/internal/database/sqlite.go @@ -124,6 +124,16 @@ func (s *SQLiteDB) GetActivities(limit, offset int) ([]Activity, error) { return activities, nil } +func (s *SQLiteDB) ActivityExists(activityID int) (bool, error) { + query := `SELECT COUNT(*) FROM activities WHERE activity_id = ?` + var count int + err := s.db.QueryRow(query, activityID).Scan(&count) + if err != nil { + return false, err + } + return count > 0, nil +} + func (s *SQLiteDB) GetActivity(activityID int) (*Activity, error) { query := ` SELECT id, activity_id, start_time, activity_type, duration, distance, diff --git a/internal/garmin/client.go b/internal/garmin/client.go index ac27157..41d7b9f 100644 --- a/internal/garmin/client.go +++ b/internal/garmin/client.go @@ -2,16 +2,14 @@ package garmin import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "time" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "time" ) type Client struct { diff --git a/internal/parser/activity.go b/internal/parser/activity.go deleted file mode 100644 index 4987dd1..0000000 --- a/internal/parser/activity.go +++ /dev/null @@ -1,23 +0,0 @@ -package parser - -import ( - "time" - - "github.com/sstent/garminsync-go/internal/models" -) - -// ActivityMetrics is now defined in internal/models - -// Parser defines the interface for activity file parsers -type Parser interface { - ParseFile(filename string) (*models.ActivityMetrics, error) -} - -// FileType represents supported file formats -type FileType string - -const ( - FIT FileType = "fit" - TCX FileType = "tcx" - GPX FileType = "gpx" -) diff --git a/internal/parser/detector.go b/internal/parser/detector.go deleted file mode 100644 index da366c2..0000000 --- a/internal/parser/detector.go +++ /dev/null @@ -1,31 +0,0 @@ -package parser - -import ( - "bytes" - "errors" -) - -var ( - // FIT file signature - fitSignature = []byte{0x0E, 0x10} // .FIT files start with 0x0E 0x10 -) - -// DetectFileType detects the file type based on its content -func DetectFileType(data []byte) (string, error) { - // Check FIT file signature - if len(data) >= 2 && bytes.Equal(data[:2], fitSignature) { - return ".fit", nil - } - - // Check TCX file signature (XML with TrainingCenterDatabase root) - if bytes.Contains(data, []byte(" root) - if bytes.Contains(data, []byte(" prev.Ele { - elevationGain += curr.Ele - prev.Ele - } - prev = curr - } - - metrics.Distance = totalDistance - metrics.ElevationGain = elevationGain - - return metrics, nil -} - - -// haversine calculates the distance between two points on Earth -func haversine(lat1, lon1, lat2, lon2 float64) float64 { - const R = 6371000 // Earth radius in meters - φ1 := lat1 * math.Pi / 180 - φ2 := lat2 * math.Pi / 180 - Δφ := (lat2 - lat1) * math.Pi / 180 - Δλ := (lon2 - lon1) * math.Pi / 180 - - a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + - math.Cos(φ1)*math.Cos(φ2)* - math.Sin(Δλ/2)*math.Sin(Δλ/2) - c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) - - return R * c -} - -func init() { - RegisterParser(".gpx", &GPXParser{}) -} diff --git a/internal/parser/parser.go b/internal/parser/parser.go new file mode 100644 index 0000000..fe2375f --- /dev/null +++ b/internal/parser/parser.go @@ -0,0 +1,65 @@ +package parser + +import ( + "bytes" + "fmt" + "os" + "time" + + "github.com/tormoder/fit" + "github.com/sstent/garminsync-go/internal/models" +) + +// Parser handles FIT file parsing +type Parser struct{} + +func NewParser() *Parser { + return &Parser{} +} + +func (p *Parser) ParseFile(filename string) (*models.ActivityMetrics, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + data, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + + return p.ParseData(data) +} + +func (p *Parser) ParseData(data []byte) (*models.ActivityMetrics, error) { + fitFile, err := fit.Decode(bytes.NewReader(data)) + if err != nil { + return nil, fmt.Errorf("failed to decode FIT file: %w", err) + } + + activity, err := fitFile.Activity() + if err != nil { + return nil, fmt.Errorf("failed to get activity from FIT: %w", err) + } + + if len(activity.Sessions) == 0 { + return nil, fmt.Errorf("no sessions found in FIT file") + } + + session := activity.Sessions[0] + metrics := &models.ActivityMetrics{ + StartTime: session.StartTime, + Duration: time.Duration(session.TotalTimerTime) * time.Second, + Distance: float64(session.TotalDistance), + AvgHeartRate: int(session.AvgHeartRate), + MaxHeartRate: int(session.MaxHeartRate), + AvgPower: int(session.AvgPower), + Calories: int(session.TotalCalories), + ElevationGain: float64(session.TotalAscent), + ElevationLoss: float64(session.TotalDescent), + Steps: 0, // FIT sessions don't include steps + } + + return metrics, nil +} diff --git a/internal/sync/sync.go b/internal/sync/sync.go index e8d01de..2c7e483 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -9,48 +9,43 @@ import ( "github.com/sstent/garminsync-go/internal/database" "github.com/sstent/garminsync-go/internal/garmin" - "github.com/sstent/garminsync-go/internal/models" "github.com/sstent/garminsync-go/internal/parser" ) -type SyncService struct { +type Syncer struct { garminClient *garmin.Client db *database.SQLiteDB dataDir string } -func NewSyncService(garminClient *garmin.Client, db *database.SQLiteDB, dataDir string) *SyncService { - return &SyncService{ +func NewSyncer(garminClient *garmin.Client, db *database.SQLiteDB, dataDir string) *Syncer { + return &Syncer{ garminClient: garminClient, db: db, dataDir: dataDir, } } -func (s *SyncService) Sync(ctx context.Context) error { - startTime := time.Now() - fmt.Printf("Starting sync at %s\n", startTime.Format(time.RFC3339)) - defer func() { - fmt.Printf("Sync completed in %s\n", time.Since(startTime)) - }() +func (s *Syncer) FullSync(ctx context.Context) error { + fmt.Println("Starting full sync...") + defer fmt.Println("Sync completed") - // 1. Fetch latest activities from Garmin + // 1. Fetch activities from Garmin activities, err := s.garminClient.GetActivities(0, 100) if err != nil { return fmt.Errorf("failed to get activities: %w", err) } - fmt.Printf("Found %d activities on Garmin\n", len(activities)) + fmt.Printf("Found %d activities\n", len(activities)) - // 2. Sync each activity - for i, activity := range activities { + // 2. Process each activity + for _, activity := range activities { select { case <-ctx.Done(): return ctx.Err() default: - fmt.Printf("[%d/%d] Processing activity %d...\n", i+1, len(activities), activity.ActivityID) + fmt.Printf("Processing activity %d...\n", activity.ActivityID) if err := s.syncActivity(&activity); err != nil { - fmt.Printf("Error syncing activity %d: %v\n", activity.ActivityID, err) - // Continue with next activity on error + fmt.Printf("Error syncing activity: %v\n", err) } } } @@ -58,103 +53,67 @@ func (s *SyncService) Sync(ctx context.Context) error { return nil } -func (s *SyncService) syncActivity(activity *garmin.GarminActivity) error { - // Check if activity exists in database - dbActivity, err := s.db.GetActivity(activity.ActivityID) - if err == nil { - // Activity exists - check if already downloaded - if dbActivity.Downloaded { - fmt.Printf("Activity %d already downloaded\n", activity.ActivityID) - return nil - } - } else { - // Activity not in database - create new record - dbActivity = &database.Activity{ - ActivityID: activity.ActivityID, - StartTime: parseTime(activity.StartTimeLocal), - } - - // Add basic info if available - if activityType, ok := activity.ActivityType["typeKey"]; ok { - dbActivity.ActivityType = activityType.(string) - } - dbActivity.Duration = int(activity.Duration) - dbActivity.Distance = activity.Distance - - if err := s.db.CreateActivity(dbActivity); err != nil { - return fmt.Errorf("failed to create activity: %w", err) - } +func (s *Syncer) syncActivity(activity *garmin.GarminActivity) error { + // Skip if already downloaded + if exists, _ := s.db.ActivityExists(activity.ActivityID); exists { + return nil } // Download the activity file (FIT format) fileData, err := s.garminClient.DownloadActivity(activity.ActivityID, "fit") if err != nil { - return fmt.Errorf("failed to download activity: %w", err) - } - - // Determine filename - filename := filepath.Join( - s.dataDir, - "activities", - fmt.Sprintf("%d_%s.fit", activity.ActivityID, activity.StartTimeLocal[:10]), - ) - - // Create directories if needed - if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { - return fmt.Errorf("failed to create directory: %w", err) + return fmt.Errorf("download failed: %w", err) } // Save file + filename := filepath.Join(s.dataDir, "activities", fmt.Sprintf("%d.fit", activity.ActivityID)) + if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { + return fmt.Errorf("directory creation failed: %w", err) + } if err := os.WriteFile(filename, fileData, 0644); err != nil { - return fmt.Errorf("failed to write file: %w", err) + return fmt.Errorf("file write failed: %w", err) } - // Parse the file to extract additional metrics - metrics, err := s.parseActivityFile(fileData, "fit") + // Parse the file + fileParser := parser.NewParser() + metrics, err := fileParser.ParseData(fileData) if err != nil { - return fmt.Errorf("failed to parse activity file: %w", err) + return fmt.Errorf("parsing failed: %w", err) } - // Update activity with parsed metrics - dbActivity.Duration = int(metrics.Duration.Seconds()) - dbActivity.Distance = metrics.Distance - dbActivity.MaxHeartRate = metrics.MaxHeartRate - dbActivity.AvgHeartRate = metrics.AvgHeartRate - dbActivity.AvgPower = metrics.AvgPower - dbActivity.Calories = metrics.Calories - dbActivity.Downloaded = true - dbActivity.Filename = filename - dbActivity.FileType = "fit" - - // Save updated activity - if err := s.db.UpdateActivity(dbActivity); err != nil { - return fmt.Errorf("failed to update activity: %w", err) + // Parse start time + startTime, err := time.Parse("2006-01-02 15:04:05", activity.StartTimeLocal) + if err != nil { + startTime = time.Now() } - fmt.Printf("Successfully synced activity %d\n", activity.ActivityID) + // Save to database + if err := s.db.CreateActivity(&database.Activity{ + ActivityID: activity.ActivityID, + StartTime: startTime, + ActivityType: getActivityType(activity), + Distance: metrics.Distance, + Duration: int(metrics.Duration.Seconds()), + MaxHeartRate: metrics.MaxHeartRate, + AvgHeartRate: metrics.AvgHeartRate, + AvgPower: float64(metrics.AvgPower), + Calories: metrics.Calories, + Filename: filename, + FileType: "fit", + Downloaded: true, + ElevationGain: metrics.ElevationGain, + Steps: metrics.Steps, + }); err != nil { + return fmt.Errorf("database error: %w", err) + } + + fmt.Printf("Synced activity %d\n", activity.ActivityID) return nil } -func (s *SyncService) parseActivityFile(fileData []byte, fileType string) (*models.ActivityMetrics, error) { - switch fileType { - case "fit": - return parser.ParseFITData(fileData) - case "tcx": - // TODO: Implement TCX parsing - return nil, fmt.Errorf("TCX parsing not implemented yet") - case "gpx": - // TODO: Implement GPX parsing - return nil, fmt.Errorf("GPX parsing not implemented yet") - default: - return nil, fmt.Errorf("unsupported file type: %s", fileType) +func getActivityType(activity *garmin.GarminActivity) string { + if activityType, ok := activity.ActivityType["typeKey"]; ok { + return activityType.(string) } -} - -func parseTime(timeStr string) time.Time { - // Garmin time format: "2023-08-15 12:30:45" - t, err := time.Parse("2006-01-02 15:04:05", timeStr) - if err != nil { - return time.Now() - } - return t + return "unknown" } diff --git a/internal/web/routes.go b/internal/web/routes.go index f5e51cf..9fe073f 100644 --- a/internal/web/routes.go +++ b/internal/web/routes.go @@ -1,103 +1,89 @@ package web import ( + "context" "net/http" - "html/template" - "path/filepath" - "os" + "strconv" + "github.com/gin-gonic/gin" "github.com/sstent/garminsync-go/internal/database" + "github.com/sstent/garminsync-go/internal/garmin" + "github.com/sstent/garminsync-go/internal/sync" ) type WebHandler struct { - db *database.SQLiteDB - templates map[string]*template.Template + db *database.SQLiteDB + syncer *sync.Syncer + garmin *garmin.Client + templates map[string]interface{} // Placeholder for template handling } -func NewWebHandler(db *database.SQLiteDB) *WebHandler { +func NewWebHandler(db *database.SQLiteDB, syncer *sync.Syncer, garmin *garmin.Client) *WebHandler { return &WebHandler{ - db: db, - templates: make(map[string]*template.Template), + db: db, + syncer: syncer, + garmin: garmin, + templates: make(map[string]interface{}), } } -func (h *WebHandler) LoadTemplates(templateDir string) error { - layouts, err := filepath.Glob(filepath.Join(templateDir, "layouts", "*.html")) - if err != nil { - return err - } - - partials, err := filepath.Glob(filepath.Join(templateDir, "partials", "*.html")) - if err != nil { - return err - } - - pages, err := filepath.Glob(filepath.Join(templateDir, "pages", "*.html")) - if err != nil { - return err - } - - for _, page := range pages { - name := filepath.Base(page) - - files := append([]string{page}, layouts...) - files = append(files, partials...) - - h.templates[name], err = template.ParseFiles(files...) - if err != nil { - return err - } - } - - return nil +func (h *WebHandler) RegisterRoutes(router *gin.Engine) { + router.GET("/", h.Index) + router.GET("/activities", h.ActivityList) + router.GET("/activities/:id", h.ActivityDetail) + router.POST("/sync", h.Sync) } -func (h *WebHandler) Index(w http.ResponseWriter, r *http.Request) { +func (h *WebHandler) Index(c *gin.Context) { stats, err := h.db.GetStats() if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + c.AbortWithStatus(http.StatusInternalServerError) return } - - h.renderTemplate(w, "index.html", stats) + + // Placeholder for template rendering + c.JSON(http.StatusOK, stats) } -func (h *WebHandler) ActivityList(w http.ResponseWriter, r *http.Request) { - activities, err := h.db.GetActivities(50, 0) +func (h *WebHandler) ActivityList(c *gin.Context) { + limit, _ := strconv.Atoi(c.Query("limit")) + offset, _ := strconv.Atoi(c.Query("offset")) + + if limit <= 0 { + limit = 50 + } + + activities, err := h.db.GetActivities(limit, offset) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + c.AbortWithStatus(http.StatusInternalServerError) return } - - h.renderTemplate(w, "activity_list.html", activities) + + c.JSON(http.StatusOK, activities) } -func (h *WebHandler) ActivityDetail(w http.ResponseWriter, r *http.Request) { - // Extract activity ID from URL params - activityID, err := strconv.Atoi(r.URL.Query().Get("id")) +func (h *WebHandler) ActivityDetail(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) if err != nil { - http.Error(w, "Invalid activity ID", http.StatusBadRequest) + c.AbortWithStatus(http.StatusBadRequest) return } - - activity, err := h.db.GetActivity(activityID) + + activity, err := h.db.GetActivity(id) if err != nil { - http.Error(w, "Activity not found", http.StatusNotFound) + c.AbortWithStatus(http.StatusNotFound) return } - - h.renderTemplate(w, "activity_detail.html", activity) + + c.JSON(http.StatusOK, activity) } -func (h *WebHandler) renderTemplate(w http.ResponseWriter, name string, data interface{}) { - tmpl, ok := h.templates[name] - if !ok { - http.Error(w, "Template not found", http.StatusInternalServerError) +func (h *WebHandler) Sync(c *gin.Context) { + err := h.syncer.FullSync(context.Background()) + if err != nil { + c.AbortWithStatus(http.StatusInternalServerError) return } - - w.Header().Set("Content-Type", "text/html; charset=utf-8") - if err := tmpl.ExecuteTemplate(w, "base", data); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } + + c.Status(http.StatusOK) } diff --git a/internal/web/templates.go b/internal/web/templates.go deleted file mode 100644 index 2f0efef..0000000 --- a/internal/web/templates.go +++ /dev/null @@ -1,47 +0,0 @@ -package web - -import ( - "html/template" - "io" - "path/filepath" - - "github.com/sstent/garminsync-go/internal/database" -) - -type WebHandler struct { - templates *template.Template - db *database.SQLiteDB -} - -func NewWebHandler(db *database.SQLiteDB) *WebHandler { - return &WebHandler{ - db: db, - } -} - -func (h *WebHandler) LoadTemplates(templatesDir string) error { - tmpl := template.New("base") - tmpl = tmpl.Funcs(template.FuncMap{}) - - // Load layouts - layouts, err := filepath.Glob(filepath.Join(templatesDir, "layouts/*.html")) - if err != nil { - return err - } - - // Load pages - pages, err := filepath.Glob(filepath.Join(templatesDir, "pages/*.html")) - if err != nil { - return err - } - - // Combine all templates - files := append(layouts, pages...) - - h.templates, err = tmpl.ParseFiles(files...) - return err -} - -func (h *WebHandler) renderTemplate(w io.Writer, name string, data interface{}) error { - return h.templates.ExecuteTemplate(w, name, data) -}