diff --git a/backend/go.mod b/backend/go.mod index f36f94d7..cacc2600 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,6 +1,8 @@ module crawlab -go 1.22.9 +go 1.23 + +toolchain go1.23.0 replace ( github.com/crawlab-team/crawlab/core => ../core @@ -49,6 +51,7 @@ require ( 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/gofrs/uuid v3.2.0+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect @@ -66,10 +69,12 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/juju/errors v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/loopfz/gadgeto v0.9.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -97,6 +102,7 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/wI2L/fizz v0.22.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect @@ -124,6 +130,7 @@ require ( google.golang.org/grpc v1.69.2 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/go-playground/validator.v9 v9.30.0 // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/backend/go.sum b/backend/go.sum index bbea437e..ebd397ca 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -61,6 +61,7 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= @@ -134,6 +135,7 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -169,8 +171,14 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc= +github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 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.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= 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/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= @@ -197,18 +205,29 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -290,6 +309,7 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -361,6 +381,11 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -377,14 +402,21 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/loopfz/gadgeto v0.9.0 h1:yrQVBgdGhAWOB+JjH98sJzYfSqpcpKzOBIEtJFcRl2s= +github.com/loopfz/gadgeto v0.9.0/go.mod h1:S3tK5SXmKY3l39rUpPZw1B/iiy1CftV13QABFhj32Ss= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -397,7 +429,9 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -440,6 +474,7 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -467,6 +502,8 @@ github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -542,8 +579,17 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 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 v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= +github.com/ugorji/go/codec v0.0.0-20190128213124-ee1426cffec0/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= 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/wI2L/fizz v0.22.0 h1:mgRA+uUdESvgsIeBFkMSS/MEIQ4EZ4I2xyRxnCqkhJY= +github.com/wI2L/fizz v0.22.0/go.mod h1:CMxMR1amz8id9wr2YUpONf+F/F9hW1cqRXxVNNuWVxE= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -612,8 +658,10 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= @@ -811,6 +859,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1074,11 +1123,17 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-playground/validator.v9 v9.26.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/go-playground/validator.v9 v9.30.0 h1:Wk0Z37oBmKj9/n+tPyBHZmeL19LaCoK3Qq48VwYENss= +gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= diff --git a/core/controllers/base.go b/core/controllers/base.go index 3b06c103..52cc3bcf 100644 --- a/core/controllers/base.go +++ b/core/controllers/base.go @@ -1,7 +1,8 @@ package controllers import ( - "github.com/crawlab-team/crawlab/core/entity" + "time" + "github.com/crawlab-team/crawlab/core/interfaces" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/mongo" @@ -23,28 +24,32 @@ type BaseController[T any] struct { actions []Action } -type GetByIdParams struct { - Id string `path:"id" description:"The ID of the item to get"` -} - -// GetAllParams represents parameters for GetAll method -type GetAllParams struct { - Query bson.M `json:"query"` - Sort bson.D `json:"sort"` -} - // GetListParams represents parameters for GetList with pagination type GetListParams struct { - Query bson.M `json:"query"` - Sort bson.D `json:"sort"` - Pagination *entity.Pagination `json:"pagination"` - All bool `query:"all" description:"Whether to get all items"` + Conditions string `query:"conditions" description:"Filter conditions. Format: [{\"key\":\"name\",\"op\":\"eq\",\"value\":\"test\"}]"` + Sort bson.D `query:"sort" description:"Sort options"` + Page int `query:"page" default:"1" description:"Page number"` + Size int `query:"size" default:"10" description:"Page size"` + All bool `query:"all" default:"false" description:"Whether to get all items"` +} + +func (ctr *BaseController[T]) GetList(_ *gin.Context, params *GetListParams) (response *ListResponse[T], err error) { + // get all if query field "all" is set true + if params.All { + return ctr.GetAll(params) + } + + return ctr.GetWithPagination(params) +} + +type GetByIdParams struct { + Id string `path:"id" description:"The ID of the item to get"` } func (ctr *BaseController[T]) GetById(_ *gin.Context, params *GetByIdParams) (response *Response[T], err error) { id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - return nil, errors.BadRequestf("invalid id: %s", params.Id) + return GetErrorResponse[T](errors.BadRequestf("invalid id format")) } model, err := ctr.modelSvc.GetById(id) @@ -52,143 +57,132 @@ func (ctr *BaseController[T]) GetById(_ *gin.Context, params *GetByIdParams) (re return nil, err } - return GetSuccessDataResponse(*model) + return GetDataResponse(*model) } -func (ctr *BaseController[T]) GetList(c *gin.Context, params *GetListParams) (response *ListResponse[T], err error) { - // get all if query field "all" is set true - all := params.All || MustGetFilterAll(c) - - // Prepare parameters - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) - - if all { - allParams := &GetAllParams{ - Query: query, - Sort: sort, - } - return ctr.GetAll(c, allParams) - } - - // get list with pagination - pagination := MustGetPagination(c) - listParams := &GetListParams{ - Query: query, - Sort: sort, - Pagination: pagination, - } - return ctr.GetWithPagination(c, listParams) +type PostParams[T any] struct { + Data T `json:"data"` } -func (ctr *BaseController[T]) Post(c *gin.Context) (response *Response[T], err error) { - var model T - if err := c.ShouldBindJSON(&model); err != nil { - return GetErrorDataResponse[T](err) - } +func (ctr *BaseController[T]) Post(c *gin.Context, params *PostParams[T]) (response *Response[T], err error) { u := GetUserFromContext(c) - m := any(&model).(interfaces.Model) + m := any(¶ms.Data).(interfaces.Model) m.SetId(primitive.NewObjectID()) m.SetCreated(u.Id) m.SetUpdated(u.Id) col := ctr.modelSvc.GetCol() res, err := col.GetCollection().InsertOne(col.GetContext(), m) if err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } result, err := ctr.modelSvc.GetById(res.InsertedID.(primitive.ObjectID)) if err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } - return GetSuccessDataResponse(*result) + return GetDataResponse(*result) } -func (ctr *BaseController[T]) PutById(c *gin.Context) (response *Response[T], err error) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) - if err != nil { - return GetErrorDataResponse[T](err) - } +type PutByIdParams[T any] struct { + Id string `path:"id" description:"The ID of the item to update"` + Data T `json:"data"` +} - var model T - if err := c.ShouldBindJSON(&model); err != nil { - return GetErrorDataResponse[T](err) +func (ctr *BaseController[T]) PutById(c *gin.Context, params *PutByIdParams[T]) (response *Response[T], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) + if err != nil { + return GetErrorResponse[T](errors.BadRequestf("invalid id format: %v", err)) } u := GetUserFromContext(c) - m := any(&model).(interfaces.Model) + m := any(¶ms.Data).(interfaces.Model) m.SetUpdated(u.Id) + if m.GetId().IsZero() { + m.SetId(id) + } - if err := ctr.modelSvc.ReplaceById(id, model); err != nil { - return GetErrorDataResponse[T](err) + if err := ctr.modelSvc.ReplaceById(id, params.Data); err != nil { + return GetErrorResponse[T](err) } result, err := ctr.modelSvc.GetById(id) if err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } - return GetSuccessDataResponse(*result) + return GetDataResponse(*result) } -func (ctr *BaseController[T]) PatchList(c *gin.Context) (res *Response[T], err error) { - type Payload struct { - Ids []primitive.ObjectID `json:"ids"` - Update bson.M `json:"update"` +type PatchParams struct { + Ids []string `json:"ids" description:"The IDs of the items to update" validate:"required"` + Update bson.M `json:"update" description:"The update object" validate:"required"` +} + +func (ctr *BaseController[T]) PatchList(c *gin.Context, params *PatchParams) (res *Response[T], err error) { + var ids []primitive.ObjectID + for _, id := range params.Ids { + objectId, err := primitive.ObjectIDFromHex(id) + if err != nil { + return GetErrorResponse[T](errors.BadRequestf("invalid id format: %v", err)) + } + ids = append(ids, objectId) } - var payload Payload - if err := c.ShouldBindJSON(&payload); err != nil { - return GetErrorDataResponse[T](err) - } + // Get user from context for updated_by + u := GetUserFromContext(c) // query query := bson.M{ "_id": bson.M{ - "$in": payload.Ids, + "$in": ids, }, } + // Add updated_by and updated_ts to the update object + updateObj := params.Update + updateObj["updated_by"] = u.Id + updateObj["updated_ts"] = time.Now() + // update - if err := ctr.modelSvc.UpdateMany(query, bson.M{"$set": payload.Update}); err != nil { - return GetErrorDataResponse[T](err) + if err := ctr.modelSvc.UpdateMany(query, bson.M{"$set": updateObj}); err != nil { + return GetErrorResponse[T](err) } // Return an empty response with success status var emptyModel T - return GetSuccessDataResponse(emptyModel) + return GetDataResponse(emptyModel) } -func (ctr *BaseController[T]) DeleteById(c *gin.Context) (res *Response[T], err error) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +type DeleteByIdParams struct { + Id string `path:"id" description:"The ID of the item to get"` +} + +func (ctr *BaseController[T]) DeleteById(c *gin.Context, params *DeleteByIdParams) (res *Response[T], err error) { + params.Id = c.Param("id") + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](errors.BadRequestf("invalid id format: %v", err)) } if err := ctr.modelSvc.DeleteById(id); err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } var emptyModel T - return GetSuccessDataResponse(emptyModel) + return GetDataResponse(emptyModel) } -func (ctr *BaseController[T]) DeleteList(c *gin.Context) (res *Response[T], err error) { - type Payload struct { - Ids []string `json:"ids"` - } - - var payload Payload - if err := c.ShouldBindJSON(&payload); err != nil { - return GetErrorDataResponse[T](err) - } +type DeleteListParams struct { + Ids []string `json:"ids" description:"The IDs of the items to delete"` +} +func (ctr *BaseController[T]) DeleteList(_ *gin.Context, params *DeleteListParams) (res *Response[T], err error) { var ids []primitive.ObjectID - for _, id := range payload.Ids { + for _, id := range params.Ids { objectId, err := primitive.ObjectIDFromHex(id) if err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } ids = append(ids, objectId) } @@ -198,16 +192,19 @@ func (ctr *BaseController[T]) DeleteList(c *gin.Context) (res *Response[T], err "$in": ids, }, }); err != nil { - return GetErrorDataResponse[T](err) + return GetErrorResponse[T](err) } var emptyModel T - return GetSuccessDataResponse(emptyModel) + return GetDataResponse(emptyModel) } // GetAll retrieves all items based on filter and sort -func (ctr *BaseController[T]) GetAll(_ *gin.Context, params *GetAllParams) (response *ListResponse[T], err error) { - query := params.Query +func (ctr *BaseController[T]) GetAll(params *GetListParams) (response *ListResponse[T], err error) { + query, err := GetFilterQueryFromListParams(params) + if err != nil { + return GetErrorListResponse[T](errors.BadRequestf("invalid request parameters: %v", err)) + } sort := params.Sort if sort == nil { sort = bson.D{{"_id", -1}} @@ -225,29 +222,23 @@ func (ctr *BaseController[T]) GetAll(_ *gin.Context, params *GetAllParams) (resp return nil, err } - return GetSuccessListResponse(models, total) + return GetListResponse(models, total) } // GetWithPagination retrieves items with pagination -func (ctr *BaseController[T]) GetWithPagination(_ *gin.Context, params *GetListParams) (response *ListResponse[T], err error) { - // params - pagination := params.Pagination - query := params.Query - sort := params.Sort - - if pagination == nil { - pagination = GetDefaultPagination() +func (ctr *BaseController[T]) GetWithPagination(params *GetListParams) (response *ListResponse[T], err error) { + query, err := GetFilterQueryFromListParams(params) + if err != nil { + return GetErrorListResponse[T](errors.BadRequestf("invalid request parameters: %v", err)) } - - // get list models, err := ctr.modelSvc.GetMany(query, &mongo.FindOptions{ - Sort: sort, - Skip: pagination.Size * (pagination.Page - 1), - Limit: pagination.Size, + Sort: params.Sort, + Skip: params.Size * (params.Page - 1), + Limit: params.Size, }) if err != nil { if errors.Is(err, mongo2.ErrNoDocuments) { - return GetSuccessListResponse[T](nil, 0) + return GetListResponse[T](nil, 0) } else { return nil, err } @@ -260,36 +251,7 @@ func (ctr *BaseController[T]) GetWithPagination(_ *gin.Context, params *GetListP } // response - return GetSuccessListResponse(models, total) -} - -// getAll is kept for backward compatibility -func (ctr *BaseController[T]) getAll(c *gin.Context) (response *ListResponse[T], err error) { - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) - - params := &GetAllParams{ - Query: query, - Sort: sort, - } - - return ctr.GetAll(c, params) -} - -// getList is kept for backward compatibility -func (ctr *BaseController[T]) getList(c *gin.Context) (response *ListResponse[T], err error) { - // params - pagination := MustGetPagination(c) - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) - - params := &GetListParams{ - Query: query, - Sort: sort, - Pagination: pagination, - } - - return ctr.GetWithPagination(c, params) + return GetListResponse(models, total) } func NewController[T any](actions ...Action) *BaseController[T] { diff --git a/core/controllers/base_file.go b/core/controllers/base_file.go index 403fe89d..fc8657ed 100644 --- a/core/controllers/base_file.go +++ b/core/controllers/base_file.go @@ -4,48 +4,54 @@ import ( "errors" "fmt" "github.com/crawlab-team/crawlab/core/fs" + "github.com/crawlab-team/crawlab/core/interfaces" "github.com/gin-gonic/gin" "io" "os" "sync" ) -func GetBaseFileListDir(rootPath string, c *gin.Context) { - path := c.Query("path") +type GetBaseFileListDirParams struct { + Path string `path:"path"` +} + +func GetBaseFileListDir(rootPath string, params *GetBaseFileListDirParams) (response *Response[[]interfaces.FsFileInfo], err error) { + path := params.Path fsSvc, err := fs.GetBaseFileFsSvc(rootPath) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[[]interfaces.FsFileInfo](err) } files, err := fsSvc.List(path) if err != nil { if !errors.Is(err, os.ErrNotExist) { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[[]interfaces.FsFileInfo](err) } } - HandleSuccessWithData(c, files) + //HandleSuccessWithData(c, files) + return GetDataResponse[[]interfaces.FsFileInfo](files) } -func GetBaseFileFile(rootPath string, c *gin.Context) { - path := c.Query("path") +type GetBaseFileFileParams struct { + Path string `path:"path"` +} + +func GetBaseFileFile(rootPath string, params *GetBaseFileFileParams) (response *Response[string], err error) { + path := params.Path fsSvc, err := fs.GetBaseFileFsSvc(rootPath) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[string](err) } data, err := fsSvc.GetFile(path) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[string](err) } - HandleSuccessWithData(c, string(data)) + return GetDataResponse[string](string(data)) } func GetBaseFileFileInfo(rootPath string, c *gin.Context) { diff --git a/core/controllers/base_test.go b/core/controllers/base_test.go index 85cb4b34..e3715938 100644 --- a/core/controllers/base_test.go +++ b/core/controllers/base_test.go @@ -4,19 +4,30 @@ import ( "bytes" "context" "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strconv" + "testing" + "time" + + "github.com/crawlab-team/crawlab/core/entity" + "github.com/crawlab-team/crawlab/core/controllers" "github.com/crawlab-team/crawlab/core/middlewares" "github.com/crawlab-team/crawlab/core/models/models" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/mongo" "github.com/crawlab-team/crawlab/core/user" + "github.com/loopfz/gadgeto/tonic" "github.com/spf13/viper" - "net/http" - "net/http/httptest" - "testing" + "github.com/stretchr/testify/require" + "github.com/wI2L/fizz" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" ) func init() { @@ -26,7 +37,12 @@ func init() { // TestModel is a simple struct to be used as a model in tests type TestModel models.TestModel +//type TestModel struct { +// Name string `json:"name" bson:"name"` +//} + var TestToken string +var TestUserId primitive.ObjectID // SetupTestDB sets up the test database func SetupTestDB() { @@ -50,11 +66,12 @@ func SetupTestDB() { panic(err) } TestToken = token + TestUserId = u.Id } // SetupRouter sets up the gin router for testing -func SetupRouter() *gin.Engine { - router := gin.Default() +func SetupRouter() *fizz.Fizz { + router := fizz.New() return router } @@ -77,7 +94,7 @@ func TestBaseController_GetById(t *testing.T) { // Set up the router router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.GET("/testmodels/:id", ctr.GetById) + router.GET("/testmodels/:id", nil, tonic.Handler(ctr.GetById, 200)) // Create a test request req, _ := http.NewRequest("GET", "/testmodels/"+id.Hex(), nil) @@ -106,30 +123,34 @@ func TestBaseController_Post(t *testing.T) { // Set up the router router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.POST("/testmodels", ctr.Post) + router.POST("/testmodels", nil, tonic.Handler(ctr.Post, 200)) // Create a test request testModel := TestModel{Name: "test"} - jsonValue, _ := json.Marshal(testModel) + requestBody := controllers.PostParams[TestModel]{ + Data: testModel, + } + jsonValue, _ := json.Marshal(requestBody) req, _ := http.NewRequest("POST", "/testmodels", bytes.NewBuffer(jsonValue)) req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() // Serve the request router.ServeHTTP(w, req) // Check the response - assert.Equal(t, http.StatusOK, w.Code) + require.Equal(t, http.StatusOK, w.Code) var response controllers.Response[TestModel] err := json.Unmarshal(w.Body.Bytes(), &response) - assert.NoError(t, err) - assert.Equal(t, "test", response.Data.Name) + require.NoError(t, err) + require.Equal(t, "test", response.Data.Name) // Check if the document was inserted into the database result, err := service.NewModelService[TestModel]().GetById(response.Data.Id) - assert.NoError(t, err) - assert.Equal(t, "test", result.Name) + require.NoError(t, err) + require.Equal(t, "test", result.Name) } func TestBaseController_DeleteById(t *testing.T) { @@ -146,7 +167,7 @@ func TestBaseController_DeleteById(t *testing.T) { // Set up the router router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.DELETE("/testmodels/:id", ctr.DeleteById) + router.DELETE("/testmodels/:id", nil, tonic.Handler(ctr.DeleteById, 200)) // Create a test request req, _ := http.NewRequest("DELETE", "/testmodels/"+id.Hex(), nil) @@ -163,3 +184,321 @@ func TestBaseController_DeleteById(t *testing.T) { _, err = service.NewModelService[TestModel]().GetById(id) assert.Error(t, err) } + +func TestBaseController_GetList(t *testing.T) { + SetupTestDB() + defer CleanupTestDB() + + // Insert test documents + modelSvc := service.NewModelService[TestModel]() + for i := 0; i < 15; i++ { + _, err := modelSvc.InsertOne(TestModel{Name: fmt.Sprintf("test%d", i)}) + assert.NoError(t, err) + } + + // Initialize the controller + ctr := controllers.NewController[TestModel]() + + // Set up the router + router := SetupRouter() + router.Use(middlewares.AuthorizationMiddleware()) + router.GET("/testmodels/list", nil, tonic.Handler(ctr.GetList, 200)) + + // Test case 1: Get with pagination + t.Run("test_get_with_pagination", func(t *testing.T) { + var testData = []struct { + Page int + ExpectedDataCount int + ExpectedTotalCount int + }{ + {1, 10, 15}, + {2, 5, 15}, + } + for _, data := range testData { + params := url.Values{} + params.Add("page", strconv.Itoa(data.Page)) + params.Add("size", "10") + requestUrl := url.URL{Path: "/testmodels/list", RawQuery: params.Encode()} + req, _ := http.NewRequest("GET", requestUrl.String(), nil) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + var response controllers.ListResponse[TestModel] + err := json.Unmarshal(w.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, data.ExpectedDataCount, len(response.Data)) + assert.Equal(t, data.ExpectedTotalCount, response.Total) + } + }) + + // Test case 2: Get all + t.Run("test_get_all", func(t *testing.T) { + params := url.Values{} + params.Add("all", "true") + requestUrl := url.URL{Path: "/testmodels/list", RawQuery: params.Encode()} + req, _ := http.NewRequest("GET", requestUrl.String(), nil) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + var response controllers.ListResponse[TestModel] + err := json.Unmarshal(w.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, 15, len(response.Data)) + assert.Equal(t, 15, response.Total) + }) + + // Test case 3: Get with query filter + t.Run("test_get_with_query_filter", func(t *testing.T) { + cond := []entity.Condition{ + {Key: "name", Op: "eq", Value: "test1"}, + } + condBytes, err := json.Marshal(cond) + require.Nil(t, err) + params := url.Values{} + params.Add("conditions", string(condBytes)) + params.Add("page", "1") + params.Add("size", "10") + requestUrl := url.URL{Path: "/testmodels/list", RawQuery: params.Encode()} + req, _ := http.NewRequest("GET", requestUrl.String(), nil) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + var response controllers.ListResponse[TestModel] + err = json.Unmarshal(w.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, 1, len(response.Data)) + assert.Equal(t, 1, response.Total) + }) +} + +func TestBaseController_PutById(t *testing.T) { + SetupTestDB() + defer CleanupTestDB() + + // Insert a test document + id, err := service.NewModelService[TestModel]().InsertOne(TestModel{Name: "test"}) + assert.NoError(t, err) + + // Initialize the controller + ctr := controllers.NewController[TestModel]() + + // Set up the router + router := SetupRouter() + router.Use(middlewares.AuthorizationMiddleware()) + router.PUT("/testmodels/:id", nil, tonic.Handler(ctr.PutById, 200)) + + // Create a test request + updatedModel := TestModel{Name: "updated"} + requestParams := controllers.PutByIdParams[TestModel]{ + Data: updatedModel, + } + jsonValue, _ := json.Marshal(requestParams) + req, _ := http.NewRequest("PUT", "/testmodels/"+id.Hex(), bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + var response controllers.Response[TestModel] + err = json.Unmarshal(w.Body.Bytes(), &response) + assert.NoError(t, err) + assert.Equal(t, "updated", response.Data.Name) + + // Check if the document was updated in the database + result, err := service.NewModelService[TestModel]().GetById(id) + assert.NoError(t, err) + assert.Equal(t, "updated", result.Name) + + // Test with invalid ID + req, _ = http.NewRequest("PUT", "/testmodels/invalid-id", bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w = httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusBadRequest, w.Code) +} + +func TestBaseController_PatchList(t *testing.T) { + SetupTestDB() + defer CleanupTestDB() + + // Insert test documents + modelSvc := service.NewModelService[TestModel]() + var ids []primitive.ObjectID + for i := 0; i < 3; i++ { + id, err := modelSvc.InsertOne(TestModel{Name: fmt.Sprintf("test%d", i)}) + assert.NoError(t, err) + ids = append(ids, id) + } + + // Initialize the controller + ctr := controllers.NewController[TestModel]() + + // Set up the router + router := SetupRouter() + router.Use(middlewares.AuthorizationMiddleware()) + router.PATCH("/testmodels", nil, tonic.Handler(ctr.PatchList, 200)) + + // Create a test request + t.Run("test_patch_list", func(t *testing.T) { + var idStrings []string + for _, id := range ids { + idStrings = append(idStrings, id.Hex()) + } + requestBody := controllers.PatchParams{ + Ids: idStrings, + Update: bson.M{"name": "patched"}, + } + jsonValue, _ := json.Marshal(requestBody) + req, _ := http.NewRequest("PATCH", "/testmodels", bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Get the user ID + userId := TestUserId + + // Record time before the update + beforeUpdate := time.Now() + + time.Sleep(100 * time.Millisecond) + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + time.Sleep(100 * time.Millisecond) + + // Record time after the update + afterUpdate := time.Now() + + // Check if the documents were updated in the database + for _, id := range ids { + result, err := modelSvc.GetById(id) + assert.NoError(t, err) + assert.Equal(t, "patched", result.Name) + + // Verify updated_by is set to the current user's ID + assert.Equal(t, userId, result.UpdatedBy) + + // Verify updated_ts is set to a recent timestamp + assert.GreaterOrEqual(t, result.UpdatedAt.UnixMilli(), beforeUpdate.UnixMilli()) + assert.LessOrEqual(t, result.UpdatedAt.UnixMilli(), afterUpdate.UnixMilli()) + } + }) + + // Test with invalid ID + t.Run("test_patch_list_with_invalid_id", func(t *testing.T) { + requestBody := controllers.PatchParams{ + Ids: []string{"invalid-id"}, + Update: bson.M{"name": "patched"}, + } + jsonValue, _ := json.Marshal(requestBody) + req, _ := http.NewRequest("PATCH", "/testmodels", bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusBadRequest, w.Code) + }) +} + +func TestBaseController_DeleteList(t *testing.T) { + SetupTestDB() + defer CleanupTestDB() + + // Insert test documents + modelSvc := service.NewModelService[TestModel]() + var ids []primitive.ObjectID + for i := 0; i < 3; i++ { + id, err := modelSvc.InsertOne(TestModel{Name: fmt.Sprintf("test%d", i)}) + assert.NoError(t, err) + ids = append(ids, id) + } + + // Initialize the controller + ctr := controllers.NewController[TestModel]() + + // Set up the router + router := SetupRouter() + router.Use(middlewares.AuthorizationMiddleware()) + router.DELETE("/testmodels", nil, tonic.Handler(ctr.DeleteList, 200)) + + // Create a test request + var idStrings []string + for _, id := range ids { + idStrings = append(idStrings, id.Hex()) + } + requestBody := controllers.DeleteListParams{ + Ids: idStrings, + } + jsonValue, _ := json.Marshal(requestBody) + req, _ := http.NewRequest("DELETE", "/testmodels", bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusOK, w.Code) + + // Check if the documents were deleted from the database + for _, id := range ids { + _, err := modelSvc.GetById(id) + assert.Error(t, err) + } + + // Test with invalid ID + requestBody = controllers.DeleteListParams{ + Ids: []string{"invalid-id"}, + } + jsonValue, _ = json.Marshal(requestBody) + req, _ = http.NewRequest("DELETE", "/testmodels", bytes.NewBuffer(jsonValue)) + req.Header.Set("Authorization", TestToken) + req.Header.Set("Content-Type", "application/json") + w = httptest.NewRecorder() + + // Serve the request + router.ServeHTTP(w, req) + + // Check the response + assert.Equal(t, http.StatusBadRequest, w.Code) +} diff --git a/core/controllers/project.go b/core/controllers/project.go index 0bd21595..8ec44378 100644 --- a/core/controllers/project.go +++ b/core/controllers/project.go @@ -1,34 +1,31 @@ package controllers import ( - "errors" "github.com/crawlab-team/crawlab/core/models/models" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/mongo" "github.com/gin-gonic/gin" + "github.com/juju/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" mongo2 "go.mongodb.org/mongo-driver/mongo" ) -func GetProjectList(c *gin.Context) { - // get all list - all := MustGetFilterAll(c) - if all { - NewController[models.Project]().getAll(c) - return +func GetProjectList(c *gin.Context, params *GetListParams) (response *ListResponse[models.Project], err error) { + if params.All { + return NewController[models.Project]().GetAll(params) } - // params - pagination := MustGetPagination(c) - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) + query, err := GetFilterQueryFromListParams(params) + if err != nil { + return GetErrorListResponse[models.Project](errors.BadRequestf("invalid request parameters: %v", err)) + } // get list projects, err := service.NewModelService[models.Project]().GetMany(query, &mongo.FindOptions{ - Sort: sort, - Skip: pagination.Size * (pagination.Page - 1), - Limit: pagination.Size, + Sort: params.Sort, + Skip: params.Size * (params.Page - 1), + Limit: params.Size, }) if err != nil { if err.Error() != mongo2.ErrNoDocuments.Error() { @@ -82,5 +79,5 @@ func GetProjectList(c *gin.Context) { projects[i].Spiders = cache[p.Id] } - HandleSuccessWithListData(c, projects, total) + return GetListResponse[models.Project](projects, total) } diff --git a/core/controllers/router.go b/core/controllers/router.go index 9fa346fd..1b7846a2 100644 --- a/core/controllers/router.go +++ b/core/controllers/router.go @@ -29,13 +29,13 @@ func GetGlobalFizzWrapper() *openapi.FizzWrapper { // NewRouterGroups initializes the router groups with their respective middleware func NewRouterGroups(app *gin.Engine) (groups *RouterGroups) { // Create OpenAPI wrapper - wrapper := openapi.NewFizzWrapper(app) + globalWrapper = openapi.NewFizzWrapper(app) return &RouterGroups{ AuthGroup: app.Group("/", middlewares.AuthorizationMiddleware()), SyncAuthGroup: app.Group("/", middlewares.SyncAuthorizationMiddleware()), AnonymousGroup: app.Group("/"), - Wrapper: wrapper, + Wrapper: globalWrapper, } } @@ -232,33 +232,6 @@ func InitRoutes(app *gin.Engine) (err error) { HandlerFunc: GetProjectList, }, }...)) - RegisterController(groups.AuthGroup, "/schedules", NewController[models.Schedule]([]Action{ - { - Method: http.MethodPost, - Path: "", - HandlerFunc: PostSchedule, - }, - { - Method: http.MethodPut, - Path: "/:id", - HandlerFunc: PutScheduleById, - }, - { - Method: http.MethodPost, - Path: "/:id/enable", - HandlerFunc: PostScheduleEnable, - }, - { - Method: http.MethodPost, - Path: "/:id/disable", - HandlerFunc: PostScheduleDisable, - }, - { - Method: http.MethodPost, - Path: "/:id/run", - HandlerFunc: PostScheduleRun, - }, - }...)) RegisterController(groups.AuthGroup, "/spiders", NewController[models.Spider]([]Action{ { Method: http.MethodGet, @@ -351,6 +324,35 @@ func InitRoutes(app *gin.Engine) (err error) { HandlerFunc: GetSpiderResults, }, }...)) + groups.AnonymousGroup.GET("/openapi.json", GetOpenAPI) + return + RegisterController(groups.AuthGroup, "/schedules", NewController[models.Schedule]([]Action{ + { + Method: http.MethodPost, + Path: "", + HandlerFunc: PostSchedule, + }, + { + Method: http.MethodPut, + Path: "/:id", + HandlerFunc: PutScheduleById, + }, + { + Method: http.MethodPost, + Path: "/:id/enable", + HandlerFunc: PostScheduleEnable, + }, + { + Method: http.MethodPost, + Path: "/:id/disable", + HandlerFunc: PostScheduleDisable, + }, + { + Method: http.MethodPost, + Path: "/:id/run", + HandlerFunc: PostScheduleRun, + }, + }...)) RegisterController(groups.AuthGroup, "/tasks", NewController[models.Task]([]Action{ { Method: http.MethodGet, diff --git a/core/controllers/router_test.go b/core/controllers/router_test.go index e222ce8c..94a40bb0 100644 --- a/core/controllers/router_test.go +++ b/core/controllers/router_test.go @@ -60,7 +60,7 @@ func TestRegisterController_Routes(t *testing.T) { func TestInitRoutes_ProjectsRoute(t *testing.T) { router := gin.Default() - controllers.InitRoutes(router) + _ = controllers.InitRoutes(router) // Check if the projects route is registered routes := router.Routes() diff --git a/core/controllers/spider.go b/core/controllers/spider.go index 07aeb47c..06008862 100644 --- a/core/controllers/spider.go +++ b/core/controllers/spider.go @@ -1,59 +1,55 @@ package controllers import ( - "errors" - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/models/models" - mongo2 "github.com/crawlab-team/crawlab/core/mongo" - "github.com/crawlab-team/crawlab/core/spider" "math" "os" "path/filepath" "sync" + "github.com/crawlab-team/crawlab/core/constants" "github.com/crawlab-team/crawlab/core/fs" "github.com/crawlab-team/crawlab/core/interfaces" + "github.com/crawlab-team/crawlab/core/models/models" "github.com/crawlab-team/crawlab/core/models/service" + mongo2 "github.com/crawlab-team/crawlab/core/mongo" + "github.com/crawlab-team/crawlab/core/spider" "github.com/crawlab-team/crawlab/core/spider/admin" "github.com/crawlab-team/crawlab/core/utils" "github.com/gin-gonic/gin" + "github.com/juju/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) -func GetSpiderById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +// GetSpiderById handles getting a spider by ID +func GetSpiderById(_ *gin.Context, params *GetByIdParams) (response *Response[models.Spider], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.Spider](errors.BadRequestf("invalid id format")) } s, err := service.NewModelService[models.Spider]().GetById(id) if errors.Is(err, mongo.ErrNoDocuments) { - HandleErrorNotFound(c, err) - return + return GetErrorResponse[models.Spider](errors.NotFoundf("spider not found")) } if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } // stat s.Stat, err = service.NewModelService[models.SpiderStat]().GetById(s.Id) if err != nil { if !errors.Is(err, mongo.ErrNoDocuments) { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } } - // data collection (compatible to old version) # TODO: remove in the future + // data collection (compatible to old version) if s.ColName == "" && !s.ColId.IsZero() { col, err := service.NewModelService[models.DataCollection]().GetById(s.ColId) if err != nil { if !errors.Is(err, mongo.ErrNoDocuments) { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } } else { s.ColName = col.Name @@ -65,55 +61,51 @@ func GetSpiderById(c *gin.Context) { s.Git, err = service.NewModelService[models.Git]().GetById(s.GitId) if err != nil { if !errors.Is(err, mongo.ErrNoDocuments) { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } } } - HandleSuccessWithData(c, s) + return GetDataResponse(*s) } -func GetSpiderList(c *gin.Context, params *GetListParams) { +// GetSpiderList handles getting a list of spiders with optional stats +func GetSpiderList(c *gin.Context, params *GetListParams) (response *ListResponse[models.Spider], err error) { // get all list - all := MustGetFilterAll(c) + all := params.All if all { - NewController[models.Spider]().getAll(c) - return + return NewController[models.Spider]().GetAll(params) } // get list withStats := c.Query("stats") if withStats == "" { - NewController[models.Spider]().GetList(c, params) - return + return NewController[models.Spider]().GetList(c, params) } // get list with stats - getSpiderListWithStats(c) + return getSpiderListWithStats(params) } -func getSpiderListWithStats(c *gin.Context) { - // params - pagination := MustGetPagination(c) - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) - +func getSpiderListWithStats(params *GetListParams) (response *ListResponse[models.Spider], err error) { + query, err := GetFilterQueryFromListParams(params) + if err != nil { + return GetErrorListResponse[models.Spider](errors.BadRequestf("invalid request parameters: %v", err)) + } // get list spiders, err := service.NewModelService[models.Spider]().GetMany(query, &mongo2.FindOptions{ - Sort: sort, - Skip: pagination.Size * (pagination.Page - 1), - Limit: pagination.Size, + Sort: params.Sort, + Skip: params.Size * (params.Page - 1), + Limit: params.Size, }) if err != nil { - if err.Error() != mongo.ErrNoDocuments.Error() { - HandleErrorInternalServerError(c, err) + if !errors.Is(err, mongo.ErrNoDocuments) { + return GetErrorListResponse[models.Spider](err) } - return + return GetListResponse[models.Spider]([]models.Spider{}, 0) } if len(spiders) == 0 { - HandleSuccessWithListData(c, []models.Spider{}, 0) - return + return GetListResponse[models.Spider]([]models.Spider{}, 0) } // ids @@ -129,15 +121,13 @@ func getSpiderListWithStats(c *gin.Context) { // total count total, err := service.NewModelService[models.Spider]().Count(query) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.Spider](err) } // stat list spiderStats, err := service.NewModelService[models.SpiderStat]().GetMany(bson.M{"_id": bson.M{"$in": ids}}, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.Spider](err) } // cache stat list to dict @@ -170,15 +160,13 @@ func getSpiderListWithStats(c *gin.Context) { } tasks, err = service.NewModelService[models.Task]().GetMany(queryTask, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.Spider](err) } // task stats list taskStats, err := service.NewModelService[models.TaskStat]().GetMany(queryTask, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.Spider](err) } // cache task stats to dict @@ -201,8 +189,7 @@ func getSpiderListWithStats(c *gin.Context) { if len(gitIds) > 0 && utils.IsPro() { gits, err = service.NewModelService[models.Git]().GetMany(bson.M{"_id": bson.M{"$in": gitIds}}, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.Spider](err) } } @@ -240,16 +227,12 @@ func getSpiderListWithStats(c *gin.Context) { } // response - HandleSuccessWithListData(c, data, total) + return GetListResponse(data, total) } -func PostSpider(c *gin.Context) { - // bind - var s models.Spider - if err := c.ShouldBindJSON(&s); err != nil { - HandleErrorBadRequest(c, err) - return - } +// PostSpider handles creating a new spider +func PostSpider(c *gin.Context, params *PostParams[models.Spider]) (response *Response[models.Spider], err error) { + s := params.Data if s.Mode == "" { s.Mode = constants.RunTypeRandom @@ -266,8 +249,7 @@ func PostSpider(c *gin.Context) { s.SetUpdated(u.Id) id, err := service.NewModelService[models.Spider]().InsertOne(s) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } s.SetId(id) @@ -278,20 +260,17 @@ func PostSpider(c *gin.Context) { st.SetUpdated(u.Id) _, err = service.NewModelService[models.SpiderStat]().InsertOne(st) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } // create folder fsSvc, err := getSpiderFsSvcById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } err = fsSvc.CreateDir(".") if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } // create template if available @@ -299,63 +278,52 @@ func PostSpider(c *gin.Context) { if templateSvc := spider.GetSpiderTemplateRegistryService(); templateSvc != nil { err = templateSvc.CreateTemplate(s.Id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } } } - HandleSuccessWithData(c, s) + return GetDataResponse(s) } -func PutSpiderById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +// PutSpiderById handles updating a spider by ID +func PutSpiderById(c *gin.Context, params *PutByIdParams[models.Spider]) (response *Response[models.Spider], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return - } - - // bind - var s models.Spider - if err := c.ShouldBindJSON(&s); err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.Spider](errors.BadRequestf("invalid id format")) } u := GetUserFromContext(c) - modelSvc := service.NewModelService[models.Spider]() - // save - s.SetUpdated(u.Id) - err = modelSvc.ReplaceById(id, s) - if err != nil { - HandleErrorInternalServerError(c, err) - return + params.Data.SetUpdated(u.Id) + if params.Data.Id.IsZero() { + params.Data.SetId(id) } - _s, err := modelSvc.GetById(id) + err = modelSvc.ReplaceById(id, params.Data) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } - s = *_s - HandleSuccessWithData(c, s) + s, err := modelSvc.GetById(id) + if err != nil { + return GetErrorResponse[models.Spider](err) + } + + return GetDataResponse(*s) } -func DeleteSpiderById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +// DeleteSpiderById handles deleting a spider by ID +func DeleteSpiderById(_ *gin.Context, params *DeleteByIdParams) (response *Response[models.Spider], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.Spider](errors.BadRequestf("invalid id format")) } - // spider s, err := service.NewModelService[models.Spider]().GetById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](errors.NotFoundf("spider not found")) } if err := mongo2.RunTransaction(func(context mongo.SessionContext) (err error) { @@ -416,14 +384,13 @@ func DeleteSpiderById(c *gin.Context) { return nil }); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } if !s.GitId.IsZero() { go func() { // delete spider directory - fsSvc, err := getSpiderFsSvcById(id) + fsSvc, err := getSpiderFsSvcById(s.Id) if err != nil { logger.Errorf("failed to get spider fs service: %v", err) return @@ -436,34 +403,39 @@ func DeleteSpiderById(c *gin.Context) { }() } - HandleSuccess(c) + return GetDataResponse(models.Spider{}) } -func DeleteSpiderList(c *gin.Context) { - var payload struct { - Ids []primitive.ObjectID `json:"ids"` - } - if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return +type DeleteSpiderListParams struct { + Ids []string `json:"ids" validate:"required"` +} + +// DeleteSpiderList handles deleting multiple spiders +func DeleteSpiderList(_ *gin.Context, params *DeleteSpiderListParams) (response *Response[models.Spider], err error) { + var ids []primitive.ObjectID + for _, id := range params.Ids { + _id, err := primitive.ObjectIDFromHex(id) + if err != nil { + return GetErrorResponse[models.Spider](errors.BadRequestf("invalid id format")) + } + ids = append(ids, _id) } // Fetch spiders before deletion spiders, err := service.NewModelService[models.Spider]().GetMany(bson.M{ "_id": bson.M{ - "$in": payload.Ids, + "$in": ids, }, }, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return nil, err } if err := mongo2.RunTransaction(func(context mongo.SessionContext) (err error) { // delete spiders if err := service.NewModelService[models.Spider]().DeleteMany(bson.M{ "_id": bson.M{ - "$in": payload.Ids, + "$in": ids, }, }); err != nil { return err @@ -472,14 +444,14 @@ func DeleteSpiderList(c *gin.Context) { // delete spider stats if err := service.NewModelService[models.SpiderStat]().DeleteMany(bson.M{ "_id": bson.M{ - "$in": payload.Ids, + "$in": ids, }, }); err != nil { return err } // related tasks - tasks, err := service.NewModelService[models.Task]().GetMany(bson.M{"spider_id": bson.M{"$in": payload.Ids}}, nil) + tasks, err := service.NewModelService[models.Task]().GetMany(bson.M{"spider_id": bson.M{"$in": ids}}, nil) if err != nil { return err } @@ -521,8 +493,7 @@ func DeleteSpiderList(c *gin.Context) { return nil }); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.Spider](err) } // Delete spider directories @@ -554,25 +525,25 @@ func DeleteSpiderList(c *gin.Context) { wg.Wait() }() - HandleSuccess(c) + return GetDataResponse(models.Spider{}) } -func GetSpiderListDir(c *gin.Context) { +func GetSpiderListDir(c *gin.Context, params *GetBaseFileListDirParams) (response *Response[[]interfaces.FsFileInfo], err error) { rootPath, err := getSpiderRootPathByContext(c) if err != nil { HandleErrorForbidden(c, err) return } - GetBaseFileListDir(rootPath, c) + return GetBaseFileListDir(rootPath, params) } -func GetSpiderFile(c *gin.Context) { +func GetSpiderFile(c *gin.Context, params *GetBaseFileFileParams) (response *Response[string], err error) { rootPath, err := getSpiderRootPathByContext(c) if err != nil { HandleErrorForbidden(c, err) return } - GetBaseFileFile(rootPath, c) + return GetBaseFileFile(rootPath, params) } func GetSpiderFileInfo(c *gin.Context) { @@ -648,18 +619,16 @@ func PostSpiderExport(c *gin.Context) { PostBaseFileExport(rootPath, c) } -func PostSpiderRun(c *gin.Context) { +func PostSpiderRun(c *gin.Context) (response *Response[[]primitive.ObjectID], err error) { id, err := primitive.ObjectIDFromHex(c.Param("id")) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[[]primitive.ObjectID](errors.BadRequestf("invalid id format")) } // options var opts interfaces.SpiderRunOptions if err := c.ShouldBindJSON(&opts); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[[]primitive.ObjectID](err) } // user @@ -670,11 +639,11 @@ func PostSpiderRun(c *gin.Context) { // schedule tasks taskIds, err := admin.GetSpiderAdminService().Schedule(id, &opts) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[[]primitive.ObjectID](err) } HandleSuccessWithData(c, taskIds) + return GetDataResponse(taskIds) } func GetSpiderResults(c *gin.Context) { @@ -733,17 +702,13 @@ func getSpiderFsSvcById(id primitive.ObjectID) (svc interfaces.FsService, err er } func getSpiderRootPathByContext(c *gin.Context) (rootPath string, err error) { - // spider id id, err := primitive.ObjectIDFromHex(c.Param("id")) if err != nil { return "", err } - - // spider s, err := service.NewModelService[models.Spider]().GetById(id) if err != nil { return "", err } - return utils.GetSpiderRootPath(s) } diff --git a/core/controllers/spider_test.go b/core/controllers/spider_test.go index 10c2f4e0..d4614222 100644 --- a/core/controllers/spider_test.go +++ b/core/controllers/spider_test.go @@ -3,11 +3,13 @@ package controllers_test import ( "bytes" "encoding/json" - "github.com/crawlab-team/crawlab/core/models/models" "net/http" "net/http/httptest" "testing" + "github.com/crawlab-team/crawlab/core/models/models" + "github.com/loopfz/gadgeto/tonic" + "github.com/crawlab-team/crawlab/core/controllers" "github.com/crawlab-team/crawlab/core/middlewares" "github.com/crawlab-team/crawlab/core/models/service" @@ -24,9 +26,9 @@ func TestCreateSpider(t *testing.T) { gin.SetMode(gin.TestMode) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.POST("/spiders", controllers.PostSpider) + router.POST("/spiders", nil, tonic.Handler(controllers.PostSpider, 200)) payload := models.Spider{ Name: "Test Spider", @@ -54,9 +56,9 @@ func TestGetSpiderById(t *testing.T) { gin.SetMode(gin.TestMode) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.GET("/spiders/:id", controllers.GetSpiderById) + router.GET("/spiders/:id", nil, tonic.Handler(controllers.GetSpiderById, 200)) model := models.Spider{ Name: "Test Spider", @@ -89,9 +91,9 @@ func TestUpdateSpiderById(t *testing.T) { gin.SetMode(gin.TestMode) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.PUT("/spiders/:id", controllers.PutSpiderById) + router.PUT("/spiders/:id", nil, tonic.Handler(controllers.PutSpiderById, 200)) model := models.Spider{ Name: "Test Spider", @@ -110,7 +112,10 @@ func TestUpdateSpiderById(t *testing.T) { ColName: "test_spider", } payload.SetId(id) - jsonValue, _ := json.Marshal(payload) + requestBody := controllers.PutByIdParams[models.Spider]{ + Data: payload, + } + jsonValue, _ := json.Marshal(requestBody) req, _ := http.NewRequest("PUT", "/spiders/"+spiderId, bytes.NewBuffer(jsonValue)) req.Header.Set("Authorization", TestToken) resp := httptest.NewRecorder() @@ -136,9 +141,9 @@ func TestDeleteSpiderById(t *testing.T) { gin.SetMode(gin.TestMode) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.DELETE("/spiders/:id", controllers.DeleteSpiderById) + router.DELETE("/spiders/:id", nil, tonic.Handler(controllers.DeleteSpiderById, 200)) model := models.Spider{ Name: "Test Spider", @@ -186,9 +191,9 @@ func TestDeleteSpiderList(t *testing.T) { gin.SetMode(gin.TestMode) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.DELETE("/spiders", controllers.DeleteSpiderList) + router.DELETE("/spiders", nil, tonic.Handler(controllers.DeleteSpiderList, 200)) modelList := []models.Spider{ { diff --git a/core/controllers/user.go b/core/controllers/user.go index 2449b26b..184a0dd2 100644 --- a/core/controllers/user.go +++ b/core/controllers/user.go @@ -1,11 +1,11 @@ package controllers import ( - "errors" - "fmt" - "github.com/crawlab-team/crawlab/core/mongo" "regexp" + "github.com/crawlab-team/crawlab/core/mongo" + "github.com/juju/errors" + "github.com/crawlab-team/crawlab/core/models/models" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/utils" @@ -15,34 +15,31 @@ import ( mongo2 "go.mongodb.org/mongo-driver/mongo" ) -func GetUserById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +func GetUserById(c *gin.Context, params *GetByIdParams) (response *Response[models.User], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { HandleErrorBadRequest(c, err) return } - getUserById(id, c) + return getUserById(id) } -func GetUserList(c *gin.Context) { - // params - pagination := MustGetPagination(c) - query := MustGetFilterQuery(c) - sort := MustGetSortOption(c) - - // get users +func GetUserList(_ *gin.Context, params *GetListParams) (response *ListResponse[models.User], err error) { + query, err := GetFilterQueryFromListParams(params) + if err != nil { + return GetErrorListResponse[models.User](err) + } users, err := service.NewModelService[models.User]().GetMany(query, &mongo.FindOptions{ - Sort: sort, - Skip: pagination.Size * (pagination.Page - 1), - Limit: pagination.Size, + Sort: params.Sort, + Skip: params.Size * (params.Page - 1), + Limit: params.Size, }) if err != nil { if errors.Is(err, mongo2.ErrNoDocuments) { - HandleSuccessWithListData(c, nil, 0) + return GetListResponse[models.User](nil, 0) } else { - HandleErrorInternalServerError(c, err) + return GetErrorListResponse[models.User](err) } - return } // get roles @@ -58,8 +55,7 @@ func GetUserList(c *gin.Context) { "_id": bson.M{"$in": roleIds}, }, nil) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.User](err) } rolesMap := make(map[primitive.ObjectID]models.Role) for _, role := range roles { @@ -80,149 +76,124 @@ func GetUserList(c *gin.Context) { // total count total, err := service.NewModelService[models.User]().Count(query) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorListResponse[models.User](err) } // response - HandleSuccessWithListData(c, users, total) + return GetListResponse[models.User](users, total) } -func PostUser(c *gin.Context) { - var payload struct { - Username string `json:"username"` - Password string `json:"password"` - Role string `json:"role"` - RoleId primitive.ObjectID `json:"role_id"` - Email string `json:"email"` - } - if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return - } +type PostUserParams struct { + Username string `json:"username" validate:"required"` + Password string `json:"password" validate:"required"` + Role string `json:"role"` + RoleId primitive.ObjectID `json:"role_id"` + Email string `json:"email"` +} +func PostUser(c *gin.Context, params *PostUserParams) (response *Response[models.User], err error) { // Validate email format - if payload.Email != "" { + if params.Email != "" { emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) - if !emailRegex.MatchString(payload.Email) { - HandleErrorBadRequest(c, fmt.Errorf("invalid email format")) - return + if !emailRegex.MatchString(params.Email) { + return GetErrorResponse[models.User](errors.BadRequestf("invalid email format")) } } - if !payload.RoleId.IsZero() { - _, err := service.NewModelService[models.Role]().GetById(payload.RoleId) + if !params.RoleId.IsZero() { + _, err := service.NewModelService[models.Role]().GetById(params.RoleId) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("role not found: %v", err)) } } u := GetUserFromContext(c) model := models.User{ - Username: payload.Username, - Password: utils.EncryptMd5(payload.Password), - Role: payload.Role, - RoleId: payload.RoleId, - Email: payload.Email, + Username: params.Username, + Password: utils.EncryptMd5(params.Password), + Role: params.Role, + RoleId: params.RoleId, + Email: params.Email, } model.SetCreated(u.Id) model.SetUpdated(u.Id) id, err := service.NewModelService[models.User]().InsertOne(model) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } result, err := service.NewModelService[models.User]().GetById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } - HandleSuccessWithData(c, result) + return GetDataResponse[models.User](*result) } -func PutUserById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +func PutUserById(c *gin.Context, params *PutByIdParams[models.User]) (response *Response[models.User], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("invalid user id: %v", err)) } - putUser(id, c) + return putUser(id, GetUserFromContext(c).Id, params.Data) } -func PostUserChangePassword(c *gin.Context) { - // get id +type PostUserChangePasswordParams struct { + Id string `path:"id"` + Password string `json:"password" validate:"required"` +} + +func PostUserChangePassword(c *gin.Context, params *PostUserChangePasswordParams) (response *Response[models.User], err error) { id, err := primitive.ObjectIDFromHex(c.Param("id")) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("invalid user id: %v", err)) } - - postUserChangePassword(id, c) + return postUserChangePassword(id, GetUserFromContext(c).Id, params.Password) } -func DeleteUserById(c *gin.Context) { - id, err := primitive.ObjectIDFromHex(c.Param("id")) +func DeleteUserById(_ *gin.Context, params *DeleteByIdParams) (response *Response[models.User], err error) { + id, err := primitive.ObjectIDFromHex(params.Id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("invalid user id: %v", err)) } user, err := service.NewModelService[models.User]().GetById(id) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } if user.RootAdmin { - HandleErrorForbidden(c, errors.New("root admin cannot be deleted")) - return + return GetErrorResponse[models.User](errors.New("root admin cannot be deleted")) } if err := service.NewModelService[models.User]().DeleteById(id); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } - HandleSuccess(c) + return GetDataResponse[models.User](models.User{}) } -func DeleteUserList(c *gin.Context) { - type Payload struct { - Ids []string `json:"ids"` - } - - var payload Payload - if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return - } - +func DeleteUserList(_ *gin.Context, params *DeleteListParams) (response *Response[models.User], err error) { // Convert string IDs to ObjectIDs var ids []primitive.ObjectID - for _, id := range payload.Ids { + for _, id := range params.Ids { objectId, err := primitive.ObjectIDFromHex(id) if err != nil { - HandleErrorBadRequest(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("invalid user id: %v", err)) } ids = append(ids, objectId) } // Check if root admin is in the list - _, err := service.NewModelService[models.User]().GetOne(bson.M{ + _, err = service.NewModelService[models.User]().GetOne(bson.M{ "_id": bson.M{ "$in": ids, }, "root_admin": true, }, nil) if err == nil { - HandleErrorForbidden(c, errors.New("root admin cannot be deleted")) - return + return GetErrorResponse[models.User](errors.New("root admin cannot be deleted")) } if !errors.Is(err, mongo2.ErrNoDocuments) { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } // Delete users @@ -231,34 +202,43 @@ func DeleteUserList(c *gin.Context) { "$in": ids, }, }); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } - HandleSuccess(c) + return GetDataResponse[models.User](models.User{}) } -func GetUserMe(c *gin.Context) { +func GetUserMe(c *gin.Context) (response *Response[models.User], err error) { u := GetUserFromContext(c) - getUserByIdWithRoutes(u.Id, c) + return getUserByIdWithRoutes(u.Id) } -func PutUserMe(c *gin.Context) { +type PutUserMeParams struct { + Data models.User `json:"data"` +} + +func PutUserMe(c *gin.Context, params *PutUserMeParams) (response *Response[models.User], err error) { u := GetUserFromContext(c) - putUser(u.Id, c) + return putUser(u.Id, u.Id, params.Data) } -func PostUserMeChangePassword(c *gin.Context) { +type PostUserMeChangePasswordParams struct { + Password string `json:"password" validate:"required"` +} + +func PostUserMeChangePassword(c *gin.Context, params *PostUserMeChangePasswordParams) (response *Response[models.User], err error) { u := GetUserFromContext(c) - postUserChangePassword(u.Id, c) + return postUserChangePassword(u.Id, u.Id, params.Password) } -func getUserById(userId primitive.ObjectID, c *gin.Context) { +func getUserById(userId primitive.ObjectID) (response *Response[models.User], err error) { // get user user, err := service.NewModelService[models.User]().GetById(userId) if err != nil { - HandleErrorInternalServerError(c, err) - return + if errors.Is(err, mongo2.ErrNoDocuments) { + return GetErrorResponse[models.User](errors.BadRequestf("user not found: %v", err)) + } + return GetErrorResponse[models.User](err) } // get role @@ -266,61 +246,58 @@ func getUserById(userId primitive.ObjectID, c *gin.Context) { if !user.RoleId.IsZero() { role, err := service.NewModelService[models.Role]().GetById(user.RoleId) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](errors.BadRequestf("role not found: %v", err)) } user.Role = role.Name user.RootAdminRole = role.RootAdmin } } - HandleSuccessWithData(c, user) + return GetDataResponse[models.User](*user) } -func getUserByIdWithRoutes(userId primitive.ObjectID, c *gin.Context) { +func getUserByIdWithRoutes(userId primitive.ObjectID) (response *Response[models.User], err error) { if !utils.IsPro() { - getUserById(userId, c) - return + return getUserById(userId) } // get user user, err := service.NewModelService[models.User]().GetById(userId) if err != nil { - HandleErrorInternalServerError(c, err) - return + if errors.Is(err, mongo2.ErrNoDocuments) { + return GetErrorResponse[models.User](errors.BadRequestf("user not found: %v", err)) + } + return GetErrorResponse[models.User](err) } // get role if !user.RoleId.IsZero() { role, err := service.NewModelService[models.Role]().GetById(user.RoleId) if err != nil { - HandleErrorInternalServerError(c, err) - return + if errors.Is(err, mongo2.ErrNoDocuments) { + return GetErrorResponse[models.User](errors.BadRequestf("role not found: %v", err)) + } + return GetErrorResponse[models.User](err) } user.Role = role.Name user.RootAdminRole = role.RootAdmin user.Routes = role.Routes } - HandleSuccessWithData(c, user) + return GetDataResponse[models.User](*user) } -func putUser(userId primitive.ObjectID, c *gin.Context) { - // get payload - var user models.User - if err := c.ShouldBindJSON(&user); err != nil { - HandleErrorBadRequest(c, err) - return - } - +func putUser(userId, by primitive.ObjectID, user models.User) (response *Response[models.User], err error) { // model service modelSvc := service.NewModelService[models.User]() // update user userDb, err := modelSvc.GetById(userId) if err != nil { - HandleErrorInternalServerError(c, err) - return + if errors.Is(err, mongo2.ErrNoDocuments) { + return GetErrorResponse[models.User](errors.BadRequestf("user not found: %v", err)) + } + return GetErrorResponse[models.User](err) } // if root admin, disallow changing username and role @@ -332,53 +309,34 @@ func putUser(userId primitive.ObjectID, c *gin.Context) { // disallow changing password user.Password = userDb.Password - // current user - u := GetUserFromContext(c) - // update user - user.SetUpdated(u.Id) + user.SetUpdated(by) if user.Id.IsZero() { user.Id = userId } if err := modelSvc.ReplaceById(userId, user); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } // handle success - HandleSuccess(c) + return GetDataResponse[models.User](user) } -func postUserChangePassword(userId primitive.ObjectID, c *gin.Context) { - // get payload - var payload struct { - Password string `json:"password"` +func postUserChangePassword(userId, by primitive.ObjectID, password string) (response *Response[models.User], err error) { + if len(password) < 5 { + return GetErrorResponse[models.User](errors.BadRequestf("password must be at least 5 characters")) } - if err := c.ShouldBindJSON(&payload); err != nil { - HandleErrorBadRequest(c, err) - return - } - if len(payload.Password) < 5 { - HandleErrorBadRequest(c, errors.New("password must be at least 5 characters")) - return - } - - // current user - u := GetUserFromContext(c) // update password userDb, err := service.NewModelService[models.User]().GetById(userId) if err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } - userDb.SetUpdated(u.Id) - userDb.Password = utils.EncryptMd5(payload.Password) + userDb.SetUpdated(by) + userDb.Password = utils.EncryptMd5(password) if err := service.NewModelService[models.User]().ReplaceById(userDb.Id, *userDb); err != nil { - HandleErrorInternalServerError(c, err) - return + return GetErrorResponse[models.User](err) } - // handle success - HandleSuccess(c) + return GetDataResponse[models.User](models.User{}) } diff --git a/core/controllers/user_test.go b/core/controllers/user_test.go index 075bd9bd..45b4e2fb 100644 --- a/core/controllers/user_test.go +++ b/core/controllers/user_test.go @@ -1,6 +1,8 @@ package controllers_test import ( + "bytes" + "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -12,7 +14,7 @@ import ( "github.com/crawlab-team/crawlab/core/models/models" "github.com/crawlab-team/crawlab/core/models/service" "github.com/crawlab-team/crawlab/core/user" - "github.com/gin-gonic/gin" + "github.com/loopfz/gadgeto/tonic" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" @@ -36,9 +38,9 @@ func TestGetUserById_Success(t *testing.T) { require.Nil(t, err) u.SetId(id) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.GET("/users/:id", controllers.GetUserById) + router.GET("/users/:id", nil, tonic.Handler(controllers.GetUserById, 200)) // Test valid ID req, err := http.NewRequest(http.MethodGet, "/users/"+id.Hex(), nil) @@ -79,9 +81,9 @@ func TestGetUserList_Success(t *testing.T) { assert.Nil(t, err) } - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.GET("/users", controllers.GetUserList) + router.GET("/users", nil, tonic.Handler(controllers.GetUserList, 200)) // Test default pagination req, err := http.NewRequest(http.MethodGet, "/users", nil) @@ -108,9 +110,9 @@ func TestPostUser_Success(t *testing.T) { SetupTestDB() defer CleanupTestDB() - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.POST("/users", controllers.PostUser) + router.POST("/users", nil, tonic.Handler(controllers.PostUser, 200)) // Test creating a new user with valid data reqBody := strings.NewReader(`{ @@ -161,9 +163,9 @@ func TestPutUserById_Success(t *testing.T) { require.Nil(t, err) u.SetId(id) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.PUT("/users/:id", controllers.PutUserById) + router.PUT("/users/:id", nil, tonic.Handler(controllers.PutUserById, 200)) // Test case 1: Regular user update reqBody := strings.NewReader(`{ @@ -214,9 +216,9 @@ func TestPostUserChangePassword_Success(t *testing.T) { require.Nil(t, err) u.SetId(id) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.POST("/users/:id/change-password", controllers.PostUserChangePassword) + router.POST("/users/:id/change-password", nil, tonic.Handler(controllers.PostUserChangePassword, 200)) // Add validation for minimum password length // Test case 1: Valid password @@ -252,9 +254,9 @@ func TestGetUserMe_Success(t *testing.T) { require.Nil(t, err) u.SetId(id) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.GET("/users/me", controllers.GetUserMe) + router.GET("/users/me", nil, tonic.Handler(controllers.GetUserMe, 200)) req, _ := http.NewRequest(http.MethodGet, "/users/me", nil) req.Header.Set("Content-Type", "application/json") @@ -288,23 +290,26 @@ func TestPutUserMe_Success(t *testing.T) { require.Nil(t, err) // Create router - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.PUT("/users/me", controllers.PutUserMe) + router.PUT("/users/me", nil, tonic.Handler(controllers.PutUserMe, 200)) // Test valid update - reqBody := strings.NewReader(`{ - "username": "updateduser", - "email": "updated@example.com" - }`) - req, err := http.NewRequest(http.MethodPut, "/users/me", reqBody) + reqParams := controllers.PutUserMeParams{ + Data: models.User{ + Username: "updateduser", + Email: "updated@example.com", + }, + } + jsonValue, _ := json.Marshal(reqParams) + req, err := http.NewRequest(http.MethodPut, "/users/me", bytes.NewBuffer(jsonValue)) assert.Nil(t, err) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", token) w := httptest.NewRecorder() router.ServeHTTP(w, req) - assert.Equal(t, http.StatusOK, w.Code) + assert.Equalf(t, http.StatusOK, w.Code, "response body: %s", w.Body.String()) // Verify the update updatedUser, err := modelSvc.GetById(id) @@ -338,9 +343,9 @@ func TestPostUserMeChangePassword_Success(t *testing.T) { require.Nil(t, err) // Create router - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.POST("/users/me/change-password", controllers.PostUserMeChangePassword) + router.POST("/users/me/change-password", nil, tonic.Handler(controllers.PostUserMeChangePassword, 200)) // Test valid password change password := "newValidPassword123" @@ -388,9 +393,9 @@ func TestDeleteUserById_Success(t *testing.T) { require.Nil(t, err) u.SetId(id) - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.DELETE("/users/:id", controllers.DeleteUserById) + router.DELETE("/users/:id", nil, tonic.Handler(controllers.DeleteUserById, 200)) // Test deleting normal user req, err := http.NewRequest(http.MethodDelete, "/users/"+id.Hex(), nil) @@ -423,7 +428,7 @@ func TestDeleteUserById_Success(t *testing.T) { w = httptest.NewRecorder() router.ServeHTTP(w, req) - assert.Equal(t, http.StatusForbidden, w.Code) + assert.Equalf(t, http.StatusBadRequest, w.Code, "response body: %s", w.Body.String()) // Test deleting with invalid ID req, err = http.NewRequest(http.MethodDelete, "/users/invalid-id", nil) @@ -460,9 +465,9 @@ func TestDeleteUserList_Success(t *testing.T) { } } - router := gin.Default() + router := SetupRouter() router.Use(middlewares.AuthorizationMiddleware()) - router.DELETE("/users", controllers.DeleteUserList) + router.DELETE("/users", nil, tonic.Handler(controllers.DeleteUserList, 200)) // Test deleting normal users reqBody := strings.NewReader(fmt.Sprintf(`{"ids":["%s","%s"]}`, @@ -492,7 +497,7 @@ func TestDeleteUserList_Success(t *testing.T) { w = httptest.NewRecorder() router.ServeHTTP(w, req) - assert.Equal(t, http.StatusForbidden, w.Code) + assert.Equalf(t, http.StatusBadRequest, w.Code, "response body: %s", w.Body.String()) // Test with mix of valid and invalid ids reqBody = strings.NewReader(fmt.Sprintf(`{"ids":["%s","invalid-id"]}`, normalUserIds[0].Hex())) diff --git a/core/controllers/utils.go b/core/controllers/utils.go new file mode 100644 index 00000000..cca52bac --- /dev/null +++ b/core/controllers/utils.go @@ -0,0 +1,331 @@ +package controllers + +import ( + "encoding/json" + "errors" + "github.com/crawlab-team/crawlab/core/constants" + "github.com/crawlab-team/crawlab/core/entity" + "github.com/crawlab-team/crawlab/core/models/models" + "github.com/crawlab-team/crawlab/core/mongo" + "github.com/crawlab-team/crawlab/core/utils" + "github.com/crawlab-team/crawlab/trace" + "github.com/gin-gonic/gin" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "net/http" + "reflect" +) + +var logger = utils.NewLogger("Controllers") + +func GetUserFromContext(c *gin.Context) (u *models.User) { + value, ok := c.Get(constants.UserContextKey) + if !ok { + return nil + } + u, ok = value.(*models.User) + if !ok { + return nil + } + return u +} + +func GetFilterQueryFromListParams(params *GetListParams) (q bson.M, err error) { + if params.Conditions == "" { + return nil, nil + } + conditions, err := GetFilterFromConditionString(params.Conditions) + if err != nil { + return nil, err + } + return utils.FilterToQuery(conditions) +} + +func GetFilterQueryFromConditionString(condStr string) (q bson.M, err error) { + conditions, err := GetFilterFromConditionString(condStr) + if err != nil { + return nil, err + } + return utils.FilterToQuery(conditions) +} + +func GetFilterFromConditionString(condStr string) (f *entity.Filter, err error) { + var conditions []*entity.Condition + if err := json.Unmarshal([]byte(condStr), &conditions); err != nil { + return nil, err + } + + for i, cond := range conditions { + v := reflect.ValueOf(cond.Value) + switch v.Kind() { + case reflect.String: + item := cond.Value.(string) + // attempt to convert object id + id, err := primitive.ObjectIDFromHex(item) + if err == nil { + conditions[i].Value = id + } else { + conditions[i].Value = item + } + case reflect.Slice, reflect.Array: + var items []interface{} + for i := 0; i < v.Len(); i++ { + vItem := v.Index(i) + item := vItem.Interface() + + // string + stringItem, ok := item.(string) + if ok { + id, err := primitive.ObjectIDFromHex(stringItem) + if err == nil { + items = append(items, id) + } else { + items = append(items, stringItem) + } + continue + } + + // default + items = append(items, item) + } + conditions[i].Value = items + default: + return nil, errors.New("invalid type") + } + } + + return &entity.Filter{ + IsOr: false, + Conditions: conditions, + }, nil +} + +// GetFilter Get entity.Filter from gin.Context +func GetFilter(c *gin.Context) (f *entity.Filter, err error) { + condStr := c.Query(constants.FilterQueryFieldConditions) + return GetFilterFromConditionString(condStr) +} + +// GetFilterQuery Get bson.M from gin.Context +func GetFilterQuery(c *gin.Context) (q bson.M, err error) { + f, err := GetFilter(c) + if err != nil { + return nil, err + } + + if f == nil { + return nil, nil + } + + // TODO: implement logic OR + + return utils.FilterToQuery(f) +} + +func MustGetFilterQuery(c *gin.Context) (q bson.M) { + q, err := GetFilterQuery(c) + if err != nil { + return nil + } + return q +} + +func getResultListQuery(c *gin.Context) (q mongo.ListQuery) { + f, err := GetFilter(c) + if err != nil { + return q + } + for _, cond := range f.Conditions { + q = append(q, mongo.ListQueryCondition{ + Key: cond.Key, + Op: cond.Op, + Value: utils.NormalizeObjectId(cond.Value), + }) + } + return q +} + +func GetDefaultPagination() (p *entity.Pagination) { + return &entity.Pagination{ + Page: constants.PaginationDefaultPage, + Size: constants.PaginationDefaultSize, + } +} + +func GetPagination(c *gin.Context) (p *entity.Pagination, err error) { + var _p entity.Pagination + if err := c.ShouldBindQuery(&_p); err != nil { + return GetDefaultPagination(), err + } + if _p.Page == 0 { + _p.Page = constants.PaginationDefaultPage + } + if _p.Size == 0 { + _p.Size = constants.PaginationDefaultSize + } + return &_p, nil +} + +func MustGetPagination(c *gin.Context) (p *entity.Pagination) { + p, err := GetPagination(c) + if err != nil || p == nil { + return GetDefaultPagination() + } + return p +} + +// GetSorts Get entity.Sort from gin.Context +func GetSorts(c *gin.Context) (sorts []entity.Sort, err error) { + // bind + sortStr := c.Query(constants.SortQueryField) + if err := json.Unmarshal([]byte(sortStr), &sorts); err != nil { + return nil, err + } + return sorts, nil +} + +// GetSortsOption Get entity.Sort from gin.Context +func GetSortsOption(c *gin.Context) (sort bson.D, err error) { + sorts, err := GetSorts(c) + if err != nil { + return nil, err + } + + if sorts == nil || len(sorts) == 0 { + return bson.D{{"_id", -1}}, nil + } + + return SortsToOption(sorts) +} + +func MustGetSortOption(c *gin.Context) (sort bson.D) { + sort, err := GetSortsOption(c) + if err != nil { + return nil + } + return sort +} + +// SortsToOption Translate entity.Sort to bson.D +func SortsToOption(sorts []entity.Sort) (sort bson.D, err error) { + sort = bson.D{} + for _, s := range sorts { + switch s.Direction { + case constants.ASCENDING: + sort = append(sort, bson.E{Key: s.Key, Value: 1}) + case constants.DESCENDING: + sort = append(sort, bson.E{Key: s.Key, Value: -1}) + } + } + if len(sort) == 0 { + sort = bson.D{{"_id", -1}} + } + return sort, nil +} + +type Response[T any] struct { + Status string `json:"status"` + Message string `json:"message"` + Data T `json:"data"` + Error string `json:"error"` +} + +type ListResponse[T any] struct { + Status string `json:"status"` + Message string `json:"message"` + Total int `json:"total"` + Data []T `json:"data"` + Error string `json:"error"` +} + +func GetDataResponse[T any](model T) (res *Response[T], err error) { + return &Response[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: model, + }, nil +} + +func GetListResponse[T any](models []T, total int) (res *ListResponse[T], err error) { + return &ListResponse[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: models, + Total: total, + }, nil +} + +func GetErrorResponse[T any](err error) (res *Response[T], err2 error) { + return &Response[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageError, + Error: err.Error(), + }, err +} + +func GetErrorListResponse[T any](err error) (res *ListResponse[T], err2 error) { + return &ListResponse[T]{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageError, + Error: err.Error(), + }, err +} + +func handleError(statusCode int, c *gin.Context, err error) { + if utils.IsDev() { + trace.PrintError(err) + } + c.AbortWithStatusJSON(statusCode, entity.Response{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageError, + Error: err.Error(), + }) +} + +func HandleError(statusCode int, c *gin.Context, err error) { + handleError(statusCode, c, err) +} + +func HandleErrorBadRequest(c *gin.Context, err error) { + HandleError(http.StatusBadRequest, c, err) +} + +func HandleErrorForbidden(c *gin.Context, err error) { + HandleError(http.StatusForbidden, c, err) +} + +func HandleErrorUnauthorized(c *gin.Context, err error) { + HandleError(http.StatusUnauthorized, c, err) +} + +func HandleErrorNotFound(c *gin.Context, err error) { + HandleError(http.StatusNotFound, c, err) +} + +func HandleErrorInternalServerError(c *gin.Context, err error) { + HandleError(http.StatusInternalServerError, c, err) +} + +func HandleSuccess(c *gin.Context) { + c.AbortWithStatusJSON(http.StatusOK, entity.Response{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + }) +} + +func HandleSuccessWithData(c *gin.Context, data interface{}) { + c.AbortWithStatusJSON(http.StatusOK, entity.Response{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: data, + }) +} + +func HandleSuccessWithListData(c *gin.Context, data interface{}, total int) { + c.AbortWithStatusJSON(http.StatusOK, entity.ListResponse{ + Status: constants.HttpResponseStatusOk, + Message: constants.HttpResponseMessageSuccess, + Data: data, + Total: total, + }) +} diff --git a/core/controllers/utils_context.go b/core/controllers/utils_context.go deleted file mode 100644 index 506c7549..00000000 --- a/core/controllers/utils_context.go +++ /dev/null @@ -1,19 +0,0 @@ -package controllers - -import ( - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/models/models" - "github.com/gin-gonic/gin" -) - -func GetUserFromContext(c *gin.Context) (u *models.User) { - value, ok := c.Get(constants.UserContextKey) - if !ok { - return nil - } - u, ok = value.(*models.User) - if !ok { - return nil - } - return u -} diff --git a/core/controllers/utils_filter.go b/core/controllers/utils_filter.go deleted file mode 100644 index 7bc5e168..00000000 --- a/core/controllers/utils_filter.go +++ /dev/null @@ -1,141 +0,0 @@ -package controllers - -import ( - "encoding/json" - errors2 "errors" - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/entity" - "github.com/crawlab-team/crawlab/core/mongo" - "github.com/crawlab-team/crawlab/core/utils" - "github.com/gin-gonic/gin" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "reflect" - "strings" -) - -// GetFilter Get entity.Filter from gin.Context -func GetFilter(c *gin.Context) (f *entity.Filter, err error) { - // bind - condStr := c.Query(constants.FilterQueryFieldConditions) - var conditions []*entity.Condition - if err := json.Unmarshal([]byte(condStr), &conditions); err != nil { - return nil, err - } - - // attempt to convert object id - for i, cond := range conditions { - v := reflect.ValueOf(cond.Value) - switch v.Kind() { - case reflect.String: - item := cond.Value.(string) - id, err := primitive.ObjectIDFromHex(item) - if err == nil { - conditions[i].Value = id - } else { - conditions[i].Value = item - } - case reflect.Slice, reflect.Array: - var items []interface{} - for i := 0; i < v.Len(); i++ { - vItem := v.Index(i) - item := vItem.Interface() - - // string - stringItem, ok := item.(string) - if ok { - id, err := primitive.ObjectIDFromHex(stringItem) - if err == nil { - items = append(items, id) - } else { - items = append(items, stringItem) - } - continue - } - - // default - items = append(items, item) - } - conditions[i].Value = items - default: - return nil, errors2.New("invalid type") - } - } - - return &entity.Filter{ - IsOr: false, - Conditions: conditions, - }, nil -} - -// GetFilterQuery Get bson.M from gin.Context -func GetFilterQuery(c *gin.Context) (q bson.M, err error) { - f, err := GetFilter(c) - if err != nil { - return nil, err - } - - if f == nil { - return nil, nil - } - - // TODO: implement logic OR - - return utils.FilterToQuery(f) -} - -func MustGetFilterQuery(c *gin.Context) (q bson.M) { - q, err := GetFilterQuery(c) - if err != nil { - return nil - } - return q -} - -// GetFilterAll Get all from gin.Context -func GetFilterAll(c *gin.Context) (res bool, err error) { - resStr := c.Query(constants.FilterQueryFieldAll) - switch strings.ToUpper(resStr) { - case "1": - return true, nil - case "0": - return false, nil - case "Y": - return true, nil - case "N": - return false, nil - case "T": - return true, nil - case "F": - return false, nil - case "TRUE": - return true, nil - case "FALSE": - return false, nil - default: - return false, errors2.New("invalid value") - } -} - -func MustGetFilterAll(c *gin.Context) (res bool) { - res, err := GetFilterAll(c) - if err != nil { - return false - } - return res -} - -func getResultListQuery(c *gin.Context) (q mongo.ListQuery) { - f, err := GetFilter(c) - if err != nil { - return q - } - for _, cond := range f.Conditions { - q = append(q, mongo.ListQueryCondition{ - Key: cond.Key, - Op: cond.Op, - Value: utils.NormalizeObjectId(cond.Value), - }) - } - return q -} diff --git a/core/controllers/utils_http.go b/core/controllers/utils_http.go deleted file mode 100644 index 54ac2987..00000000 --- a/core/controllers/utils_http.go +++ /dev/null @@ -1,110 +0,0 @@ -package controllers - -import ( - "net/http" - - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/entity" - "github.com/crawlab-team/crawlab/core/utils" - "github.com/crawlab-team/crawlab/trace" - "github.com/gin-gonic/gin" -) - -type Response[T any] struct { - Status string `json:"status"` - Message string `json:"message"` - Data T `json:"data"` - Error string `json:"error"` -} - -type ListResponse[T any] struct { - Status string `json:"status"` - Message string `json:"message"` - Total int `json:"total"` - Data []T `json:"data"` - Error string `json:"error"` -} - -func GetSuccessDataResponse[T any](model T) (res *Response[T], err error) { - return &Response[T]{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageSuccess, - Data: model, - }, nil -} - -func GetSuccessListResponse[T any](models []T, total int) (res *ListResponse[T], err error) { - return &ListResponse[T]{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageSuccess, - Data: models, - Total: total, - }, nil -} - -func GetErrorDataResponse[T any](err error) (res *Response[T], err2 error) { - return &Response[T]{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageError, - Error: err.Error(), - }, err -} - -func handleError(statusCode int, c *gin.Context, err error) { - if utils.IsDev() { - trace.PrintError(err) - } - c.AbortWithStatusJSON(statusCode, entity.Response{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageError, - Error: err.Error(), - }) -} - -func HandleError(statusCode int, c *gin.Context, err error) { - handleError(statusCode, c, err) -} - -func HandleErrorBadRequest(c *gin.Context, err error) { - HandleError(http.StatusBadRequest, c, err) -} - -func HandleErrorForbidden(c *gin.Context, err error) { - HandleError(http.StatusForbidden, c, err) -} - -func HandleErrorUnauthorized(c *gin.Context, err error) { - HandleError(http.StatusUnauthorized, c, err) -} - -func HandleErrorNotFound(c *gin.Context, err error) { - HandleError(http.StatusNotFound, c, err) -} - -func HandleErrorInternalServerError(c *gin.Context, err error) { - HandleError(http.StatusInternalServerError, c, err) -} - -func HandleSuccess(c *gin.Context) { - c.AbortWithStatusJSON(http.StatusOK, entity.Response{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageSuccess, - }) -} - -func HandleSuccessWithData(c *gin.Context, data interface{}) { - c.AbortWithStatusJSON(http.StatusOK, entity.Response{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageSuccess, - Data: data, - }) -} - -func HandleSuccessWithListData(c *gin.Context, data interface{}, total int) { - c.AbortWithStatusJSON(http.StatusOK, entity.ListResponse{ - Status: constants.HttpResponseStatusOk, - Message: constants.HttpResponseMessageSuccess, - Data: data, - Total: total, - }) -} diff --git a/core/controllers/utils_logger.go b/core/controllers/utils_logger.go deleted file mode 100644 index 002ff743..00000000 --- a/core/controllers/utils_logger.go +++ /dev/null @@ -1,5 +0,0 @@ -package controllers - -import "github.com/crawlab-team/crawlab/core/utils" - -var logger = utils.NewLogger("Controllers") diff --git a/core/controllers/utils_pagination.go b/core/controllers/utils_pagination.go deleted file mode 100644 index 4f148ecf..00000000 --- a/core/controllers/utils_pagination.go +++ /dev/null @@ -1,36 +0,0 @@ -package controllers - -import ( - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/entity" - "github.com/gin-gonic/gin" -) - -func GetDefaultPagination() (p *entity.Pagination) { - return &entity.Pagination{ - Page: constants.PaginationDefaultPage, - Size: constants.PaginationDefaultSize, - } -} - -func GetPagination(c *gin.Context) (p *entity.Pagination, err error) { - var _p entity.Pagination - if err := c.ShouldBindQuery(&_p); err != nil { - return GetDefaultPagination(), err - } - if _p.Page == 0 { - _p.Page = constants.PaginationDefaultPage - } - if _p.Size == 0 { - _p.Size = constants.PaginationDefaultSize - } - return &_p, nil -} - -func MustGetPagination(c *gin.Context) (p *entity.Pagination) { - p, err := GetPagination(c) - if err != nil || p == nil { - return GetDefaultPagination() - } - return p -} diff --git a/core/controllers/utils_sort.go b/core/controllers/utils_sort.go deleted file mode 100644 index 86e68e96..00000000 --- a/core/controllers/utils_sort.go +++ /dev/null @@ -1,58 +0,0 @@ -package controllers - -import ( - "encoding/json" - "github.com/crawlab-team/crawlab/core/constants" - "github.com/crawlab-team/crawlab/core/entity" - "github.com/gin-gonic/gin" - "go.mongodb.org/mongo-driver/bson" -) - -// GetSorts Get entity.Sort from gin.Context -func GetSorts(c *gin.Context) (sorts []entity.Sort, err error) { - // bind - sortStr := c.Query(constants.SortQueryField) - if err := json.Unmarshal([]byte(sortStr), &sorts); err != nil { - return nil, err - } - return sorts, nil -} - -// GetSortsOption Get entity.Sort from gin.Context -func GetSortsOption(c *gin.Context) (sort bson.D, err error) { - sorts, err := GetSorts(c) - if err != nil { - return nil, err - } - - if sorts == nil || len(sorts) == 0 { - return bson.D{{"_id", -1}}, nil - } - - return SortsToOption(sorts) -} - -func MustGetSortOption(c *gin.Context) (sort bson.D) { - sort, err := GetSortsOption(c) - if err != nil { - return nil - } - return sort -} - -// SortsToOption Translate entity.Sort to bson.D -func SortsToOption(sorts []entity.Sort) (sort bson.D, err error) { - sort = bson.D{} - for _, s := range sorts { - switch s.Direction { - case constants.ASCENDING: - sort = append(sort, bson.E{Key: s.Key, Value: 1}) - case constants.DESCENDING: - sort = append(sort, bson.E{Key: s.Key, Value: -1}) - } - } - if len(sort) == 0 { - sort = bson.D{{"_id", -1}} - } - return sort, nil -} diff --git a/core/controllers/ws_writer.go b/core/controllers/ws_writer.go deleted file mode 100644 index dcc096c3..00000000 --- a/core/controllers/ws_writer.go +++ /dev/null @@ -1,54 +0,0 @@ -package controllers - -import ( - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" - "io" - http2 "net/http" -) - -type WsWriter struct { - io.Writer - io.Closer - conn *websocket.Conn -} - -func (w *WsWriter) Write(data []byte) (n int, err error) { - logger.Infof("websocket write: %s", string(data)) - err = w.conn.WriteMessage(websocket.TextMessage, data) - if err != nil { - return 0, err - } - return len(data), nil -} - -func (w *WsWriter) Close() (err error) { - return w.conn.Close() -} - -func (w *WsWriter) CloseWithText(text string) { - _ = w.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, text)) -} - -func (w *WsWriter) CloseWithError(err error) { - _ = w.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, err.Error())) -} - -func NewWsWriter(c *gin.Context) (writer *WsWriter, err error) { - upgrader := websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http2.Request) bool { - return true - }, - } - - conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - logger.Errorf("websocket open connection error: %v", err) - } - - return &WsWriter{ - conn: conn, - }, nil -} diff --git a/core/go.mod b/core/go.mod index c44dee01..4f0f91f6 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,11 +1,12 @@ module github.com/crawlab-team/crawlab/core -go 1.23 +go 1.23.7 replace ( github.com/crawlab-team/crawlab/grpc => ../grpc github.com/crawlab-team/crawlab/trace => ../trace github.com/crawlab-team/crawlab/vcs => ../vcs + github.com/wI2L/fizz => github.com/crawlab-team/fizz v0.0.0-20250312082934-630a98f9d791 ) require ( @@ -22,22 +23,22 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 github.com/gomarkdown/markdown v0.0.0-20241105142532-d03b89096d81 github.com/google/uuid v1.6.0 - github.com/gorilla/websocket v1.5.3 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-uuid v1.0.3 github.com/juju/errors v1.0.0 + github.com/loopfz/gadgeto v0.9.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.19.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/wI2L/fizz v0.22.0 go.mongodb.org/mongo-driver v1.15.1 golang.org/x/oauth2 v0.23.0 - golang.org/x/text v0.21.0 + golang.org/x/text v0.23.0 google.golang.org/api v0.189.0 google.golang.org/grpc v1.69.2 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -55,16 +56,16 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/bytedance/sonic v1.13.1 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.0.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-git/go-git/v5 v5.12.0 // indirect @@ -73,8 +74,8 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/go-playground/validator/v10 v10.25.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect @@ -88,16 +89,15 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/loopfz/gadgeto v0.9.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -127,14 +127,14 @@ require ( go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/arch v0.15.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/go-playground/validator.v9 v9.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/core/go.sum b/core/go.sum index 0c7649e5..33aa2bc2 100644 --- a/core/go.sum +++ b/core/go.sum @@ -99,8 +99,12 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 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 v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= +github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= 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/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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -119,6 +123,8 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -136,6 +142,8 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crawlab-team/fizz v0.0.0-20250312082934-630a98f9d791 h1:K849MMP329dseKSpMqX3XDTccpWxghMwWyrDclKzkdM= +github.com/crawlab-team/fizz v0.0.0-20250312082934-630a98f9d791/go.mod h1:CMxMR1amz8id9wr2YUpONf+F/F9hW1cqRXxVNNuWVxE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -171,12 +179,16 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc= github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 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-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= +github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= @@ -223,9 +235,13 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= +github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -323,8 +339,6 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -383,6 +397,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -395,6 +411,8 @@ github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -471,6 +489,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -561,6 +581,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= @@ -587,8 +608,6 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= 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/wI2L/fizz v0.22.0 h1:mgRA+uUdESvgsIeBFkMSS/MEIQ4EZ4I2xyRxnCqkhJY= -github.com/wI2L/fizz v0.22.0/go.mod h1:CMxMR1amz8id9wr2YUpONf+F/F9hW1cqRXxVNNuWVxE= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -647,6 +666,8 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= +golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -666,6 +687,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -756,6 +779,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -790,6 +815,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -871,6 +898,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -879,6 +908,7 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -895,6 +925,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1112,6 +1144,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= diff --git a/core/models/models/base.go b/core/models/models/base.go index 1bdb06a6..96f278ed 100644 --- a/core/models/models/base.go +++ b/core/models/models/base.go @@ -7,7 +7,7 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) -type BaseModel[T any] struct { +type BaseModel struct { Id primitive.ObjectID `json:"_id" bson:"_id"` CreatedAt time.Time `json:"created_ts,omitempty" bson:"created_ts,omitempty"` CreatedBy primitive.ObjectID `json:"created_by,omitempty" bson:"created_by,omitempty"` @@ -15,52 +15,52 @@ type BaseModel[T any] struct { UpdatedBy primitive.ObjectID `json:"updated_by,omitempty" bson:"updated_by,omitempty"` } -func (m *BaseModel[T]) GetId() primitive.ObjectID { +func (m *BaseModel) GetId() primitive.ObjectID { return m.Id } -func (m *BaseModel[T]) SetId(id primitive.ObjectID) { +func (m *BaseModel) SetId(id primitive.ObjectID) { m.Id = id } -func (m *BaseModel[T]) GetCreatedAt() time.Time { +func (m *BaseModel) GetCreatedAt() time.Time { return m.CreatedAt } -func (m *BaseModel[T]) SetCreatedAt(t time.Time) { +func (m *BaseModel) SetCreatedAt(t time.Time) { m.CreatedAt = t } -func (m *BaseModel[T]) GetCreatedBy() primitive.ObjectID { +func (m *BaseModel) GetCreatedBy() primitive.ObjectID { return m.CreatedBy } -func (m *BaseModel[T]) SetCreatedBy(id primitive.ObjectID) { +func (m *BaseModel) SetCreatedBy(id primitive.ObjectID) { m.CreatedBy = id } -func (m *BaseModel[T]) GetUpdatedAt() time.Time { +func (m *BaseModel) GetUpdatedAt() time.Time { return m.UpdatedAt } -func (m *BaseModel[T]) SetUpdatedAt(t time.Time) { +func (m *BaseModel) SetUpdatedAt(t time.Time) { m.UpdatedAt = t } -func (m *BaseModel[T]) GetUpdatedBy() primitive.ObjectID { +func (m *BaseModel) GetUpdatedBy() primitive.ObjectID { return m.UpdatedBy } -func (m *BaseModel[T]) SetUpdatedBy(id primitive.ObjectID) { +func (m *BaseModel) SetUpdatedBy(id primitive.ObjectID) { m.UpdatedBy = id } -func (m *BaseModel[T]) SetCreated(id primitive.ObjectID) { +func (m *BaseModel) SetCreated(id primitive.ObjectID) { m.SetCreatedAt(time.Now()) m.SetCreatedBy(id) } -func (m *BaseModel[T]) SetUpdated(id primitive.ObjectID) { +func (m *BaseModel) SetUpdated(id primitive.ObjectID) { m.SetUpdatedAt(time.Now()) m.SetUpdatedBy(id) } diff --git a/core/models/models/data_collection.go b/core/models/models/data_collection.go index 587b5db9..89cfbb5a 100644 --- a/core/models/models/data_collection.go +++ b/core/models/models/data_collection.go @@ -5,11 +5,11 @@ import ( ) type DataCollection struct { - any `collection:"data_collections"` - BaseModel[DataCollection] `bson:",inline"` - Name string `json:"name" bson:"name"` - Fields []entity.DataField `json:"fields" bson:"fields"` - Dedup struct { + any `collection:"data_collections"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Fields []entity.DataField `json:"fields" bson:"fields"` + Dedup struct { Enabled bool `json:"enabled" bson:"enabled"` Keys []string `json:"keys" bson:"keys"` Type string `json:"type" bson:"type"` diff --git a/core/models/models/database.go b/core/models/models/database.go index c510b0f7..a4faa23e 100644 --- a/core/models/models/database.go +++ b/core/models/models/database.go @@ -5,23 +5,23 @@ import ( ) type Database struct { - any `collection:"databases"` - BaseModel[Database] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - DataSource string `json:"data_source" bson:"data_source"` - Host string `json:"host" bson:"host"` - Port int `json:"port" bson:"port"` - URI string `json:"uri,omitempty" bson:"uri,omitempty"` - Database string `json:"database,omitempty" bson:"database,omitempty"` - Username string `json:"username,omitempty" bson:"username,omitempty"` - Password string `json:"password,omitempty" bson:"-"` - EncryptedPassword string `json:"-,omitempty" bson:"encrypted_password,omitempty"` - Status string `json:"status" bson:"status"` - Error string `json:"error" bson:"error"` - Active bool `json:"active" bson:"active"` - ActiveAt time.Time `json:"active_ts" bson:"active_ts"` - IsDefault bool `json:"is_default" bson:"-"` + any `collection:"databases"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + DataSource string `json:"data_source" bson:"data_source"` + Host string `json:"host" bson:"host"` + Port int `json:"port" bson:"port"` + URI string `json:"uri,omitempty" bson:"uri,omitempty"` + Database string `json:"database,omitempty" bson:"database,omitempty"` + Username string `json:"username,omitempty" bson:"username,omitempty"` + Password string `json:"password,omitempty" bson:"-"` + EncryptedPassword string `json:"-,omitempty" bson:"encrypted_password,omitempty"` + Status string `json:"status" bson:"status"` + Error string `json:"error" bson:"error"` + Active bool `json:"active" bson:"active"` + ActiveAt time.Time `json:"active_ts" bson:"active_ts"` + IsDefault bool `json:"is_default" bson:"-"` MongoParams *struct { AuthSource string `json:"auth_source,omitempty" bson:"auth_source,omitempty"` diff --git a/core/models/models/database_metric.go b/core/models/models/database_metric.go index 195615a0..689d0365 100644 --- a/core/models/models/database_metric.go +++ b/core/models/models/database_metric.go @@ -3,22 +3,22 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type DatabaseMetric struct { - any `collection:"database_metrics"` - BaseModel[DatabaseMetric] `bson:",inline"` - DatabaseId primitive.ObjectID `json:"database_id" bson:"database_id"` - CpuUsagePercent float32 `json:"cpu_usage_percent" bson:"cpu_usage_percent"` - TotalMemory uint64 `json:"total_memory" bson:"total_memory"` - AvailableMemory uint64 `json:"available_memory" bson:"available_memory"` - UsedMemory uint64 `json:"used_memory" bson:"used_memory"` - UsedMemoryPercent float32 `json:"used_memory_percent" bson:"used_memory_percent"` - TotalDisk uint64 `json:"total_disk" bson:"total_disk"` - AvailableDisk uint64 `json:"available_disk" bson:"available_disk"` - UsedDisk uint64 `json:"used_disk" bson:"used_disk"` - UsedDiskPercent float32 `json:"used_disk_percent" bson:"used_disk_percent"` - Connections int `json:"connections" bson:"connections"` - QueryPerSecond float64 `json:"query_per_second" bson:"query_per_second"` - TotalQuery uint64 `json:"total_query,omitempty" bson:"total_query,omitempty"` - CacheHitRatio float64 `json:"cache_hit_ratio" bson:"cache_hit_ratio"` - ReplicationLag float64 `json:"replication_lag" bson:"replication_lag"` - LockWaitTime float64 `json:"lock_wait_time" bson:"lock_wait_time"` + any `collection:"database_metrics"` + BaseModel `bson:",inline"` + DatabaseId primitive.ObjectID `json:"database_id" bson:"database_id"` + CpuUsagePercent float32 `json:"cpu_usage_percent" bson:"cpu_usage_percent"` + TotalMemory uint64 `json:"total_memory" bson:"total_memory"` + AvailableMemory uint64 `json:"available_memory" bson:"available_memory"` + UsedMemory uint64 `json:"used_memory" bson:"used_memory"` + UsedMemoryPercent float32 `json:"used_memory_percent" bson:"used_memory_percent"` + TotalDisk uint64 `json:"total_disk" bson:"total_disk"` + AvailableDisk uint64 `json:"available_disk" bson:"available_disk"` + UsedDisk uint64 `json:"used_disk" bson:"used_disk"` + UsedDiskPercent float32 `json:"used_disk_percent" bson:"used_disk_percent"` + Connections int `json:"connections" bson:"connections"` + QueryPerSecond float64 `json:"query_per_second" bson:"query_per_second"` + TotalQuery uint64 `json:"total_query,omitempty" bson:"total_query,omitempty"` + CacheHitRatio float64 `json:"cache_hit_ratio" bson:"cache_hit_ratio"` + ReplicationLag float64 `json:"replication_lag" bson:"replication_lag"` + LockWaitTime float64 `json:"lock_wait_time" bson:"lock_wait_time"` } diff --git a/core/models/models/dependency.go b/core/models/models/dependency.go index 4099cda6..6514bc20 100644 --- a/core/models/models/dependency.go +++ b/core/models/models/dependency.go @@ -5,15 +5,15 @@ import ( ) type Dependency struct { - any `collection:"dependencies"` - BaseModel[Dependency] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` - Type string `json:"type" bson:"type"` - Version string `json:"version" bson:"version"` - Status string `json:"status" bson:"status"` - Error string `json:"error,omitempty" bson:"error,omitempty"` - NodeIds []primitive.ObjectID `json:"node_ids,omitempty" bson:"-"` - Versions []string `json:"versions,omitempty" bson:"-"` + any `collection:"dependencies"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` + Type string `json:"type" bson:"type"` + Version string `json:"version" bson:"version"` + Status string `json:"status" bson:"status"` + Error string `json:"error,omitempty" bson:"error,omitempty"` + NodeIds []primitive.ObjectID `json:"node_ids,omitempty" bson:"-"` + Versions []string `json:"versions,omitempty" bson:"-"` } diff --git a/core/models/models/dependency_config.go b/core/models/models/dependency_config.go index d05a5f34..29c4c8b6 100644 --- a/core/models/models/dependency_config.go +++ b/core/models/models/dependency_config.go @@ -1,14 +1,14 @@ package models type DependencyConfig struct { - any `collection:"dependency_configs"` - BaseModel[DependencyConfig] `bson:",inline"` - Key string `json:"key" bson:"key"` - Name string `json:"name" bson:"name"` - ExecCmd string `json:"exec_cmd" bson:"exec_cmd"` - PkgCmd string `json:"pkg_cmd" bson:"pkg_cmd"` - PkgSrcURL string `json:"pkg_src_url" bson:"pkg_src_url"` - Setup bool `json:"setup" bson:"-"` - TotalDependencies int `json:"total_dependencies" bson:"-"` - SearchReady bool `json:"search_ready" bson:"-"` + any `collection:"dependency_configs"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` + Name string `json:"name" bson:"name"` + ExecCmd string `json:"exec_cmd" bson:"exec_cmd"` + PkgCmd string `json:"pkg_cmd" bson:"pkg_cmd"` + PkgSrcURL string `json:"pkg_src_url" bson:"pkg_src_url"` + Setup bool `json:"setup" bson:"-"` + TotalDependencies int `json:"total_dependencies" bson:"-"` + SearchReady bool `json:"search_ready" bson:"-"` } diff --git a/core/models/models/dependency_config_setup.go b/core/models/models/dependency_config_setup.go index 20808a85..386141ae 100644 --- a/core/models/models/dependency_config_setup.go +++ b/core/models/models/dependency_config_setup.go @@ -3,15 +3,15 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type DependencyConfigSetup struct { - any `collection:"dependency_config_setups"` - BaseModel[DependencyConfigSetup] `bson:",inline"` - DependencyConfigId primitive.ObjectID `json:"dependency_config_id" bson:"dependency_config_id"` - NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` - Version string `json:"version" bson:"version"` - Drivers []DependencyDriver `json:"versions,omitempty" bson:"versions,omitempty"` - Status string `json:"status" bson:"status"` - Error string `json:"error,omitempty" bson:"error,omitempty"` - Node *Node `json:"node,omitempty" bson:"-"` + any `collection:"dependency_config_setups"` + BaseModel `bson:",inline"` + DependencyConfigId primitive.ObjectID `json:"dependency_config_id" bson:"dependency_config_id"` + NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` + Version string `json:"version" bson:"version"` + Drivers []DependencyDriver `json:"versions,omitempty" bson:"versions,omitempty"` + Status string `json:"status" bson:"status"` + Error string `json:"error,omitempty" bson:"error,omitempty"` + Node *Node `json:"node,omitempty" bson:"-"` } type DependencyDriver struct { Name string `json:"name" bson:"name"` diff --git a/core/models/models/dependency_log.go b/core/models/models/dependency_log.go index 415245ce..e4e13976 100644 --- a/core/models/models/dependency_log.go +++ b/core/models/models/dependency_log.go @@ -3,8 +3,8 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type DependencyLog struct { - any `collection:"dependency_logs"` - BaseModel[DependencyLog] `bson:",inline"` - TargetId primitive.ObjectID `json:"target_id" bson:"target_id"` - Content string `json:"content" bson:"content"` + any `collection:"dependency_logs"` + BaseModel `bson:",inline"` + TargetId primitive.ObjectID `json:"target_id" bson:"target_id"` + Content string `json:"content" bson:"content"` } diff --git a/core/models/models/dependency_pypi_project.go b/core/models/models/dependency_pypi_project.go index f0d20a92..ec3947a3 100644 --- a/core/models/models/dependency_pypi_project.go +++ b/core/models/models/dependency_pypi_project.go @@ -1,9 +1,9 @@ package models type DependencyPypiProject struct { - any `collection:"dependency_pypi_projects"` - BaseModel[DependencyPypiProject] `bson:",inline"` - Name string `json:"name" bson:"name"` - Version string `json:"version" bson:"version"` - LastSerial int `json:"_last-serial" bson:"last_serial"` + any `collection:"dependency_pypi_projects"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Version string `json:"version" bson:"version"` + LastSerial int `json:"_last-serial" bson:"last_serial"` } diff --git a/core/models/models/dependency_repo.go b/core/models/models/dependency_repo.go index d205dce0..d3ca1263 100644 --- a/core/models/models/dependency_repo.go +++ b/core/models/models/dependency_repo.go @@ -1,10 +1,10 @@ package models type DependencyRepo struct { - any `collection:"dependency_repos"` - BaseModel[DependencyRepo] `bson:",inline"` - Name string `json:"name" bson:"name"` - Type string `json:"type" bson:"type"` - LatestVersion string `json:"latest_version" bson:"latest_version"` - AllVersions []string `json:"all_versions" bson:"all_versions"` + any `collection:"dependency_repos"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Type string `json:"type" bson:"type"` + LatestVersion string `json:"latest_version" bson:"latest_version"` + AllVersions []string `json:"all_versions" bson:"all_versions"` } diff --git a/core/models/models/environment.go b/core/models/models/environment.go index 889d412c..3faf8547 100644 --- a/core/models/models/environment.go +++ b/core/models/models/environment.go @@ -1,8 +1,8 @@ package models type Environment struct { - any `collection:"environments"` - BaseModel[Environment] `bson:",inline"` - Key string `json:"key" bson:"key"` - Value string `json:"value" bson:"value"` + any `collection:"environments"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` + Value string `json:"value" bson:"value"` } diff --git a/core/models/models/git.go b/core/models/models/git.go index 45a3365f..c5babffd 100644 --- a/core/models/models/git.go +++ b/core/models/models/git.go @@ -6,20 +6,20 @@ import ( ) type Git struct { - any `collection:"gits"` - BaseModel[Git] `bson:",inline"` - Url string `json:"url" bson:"url"` - Name string `json:"name" bson:"name"` - AuthType string `json:"auth_type" bson:"auth_type"` - Username string `json:"username" bson:"username"` - Password string `json:"password" bson:"password"` - CurrentBranch string `json:"current_branch" bson:"current_branch"` - Status string `json:"status" bson:"status"` - Error string `json:"error" bson:"error"` - Spiders []Spider `json:"spiders,omitempty" bson:"-"` - Refs []vcs.GitRef `json:"refs" bson:"refs"` - RefsUpdatedAt time.Time `json:"refs_updated_at" bson:"refs_updated_at"` - CloneLogs []string `json:"clone_logs,omitempty" bson:"clone_logs"` + any `collection:"gits"` + BaseModel `bson:",inline"` + Url string `json:"url" bson:"url"` + Name string `json:"name" bson:"name"` + AuthType string `json:"auth_type" bson:"auth_type"` + Username string `json:"username" bson:"username"` + Password string `json:"password" bson:"password"` + CurrentBranch string `json:"current_branch" bson:"current_branch"` + Status string `json:"status" bson:"status"` + Error string `json:"error" bson:"error"` + Spiders []Spider `json:"spiders,omitempty" bson:"-"` + Refs []vcs.GitRef `json:"refs" bson:"refs"` + RefsUpdatedAt time.Time `json:"refs_updated_at" bson:"refs_updated_at"` + CloneLogs []string `json:"clone_logs,omitempty" bson:"clone_logs"` // settings AutoPull bool `json:"auto_pull" bson:"auto_pull"` diff --git a/core/models/models/llm_provider.go b/core/models/models/llm_provider.go index 1364c54c..d723eb91 100644 --- a/core/models/models/llm_provider.go +++ b/core/models/models/llm_provider.go @@ -2,17 +2,17 @@ package models // LLMProvider represents a language model provider such as OpenAI, Anthropic, etc. type LLMProvider struct { - any `collection:"llm_providers"` - BaseModel[LLMProvider] `bson:",inline"` - Key string `json:"key" bson:"key"` // Provider key (e.g., "openai", "anthropic", "gemini") - Name string `json:"name" bson:"name"` // Display name for UI - Enabled bool `json:"enabled" bson:"enabled"` // Whether this provider is enabled - ApiKey string `json:"api_key" bson:"api_key"` // API key for the provider - ApiBaseUrl string `json:"api_base_url" bson:"api_base_url"` // API base URL for the provider - DeploymentName string `json:"deployment_name" bson:"deployment_name"` // Deployment name for the provider - ApiVersion string `json:"api_version" bson:"api_version"` // API version for the provider - Models []string `json:"models" bson:"models"` // Models supported by this provider - Unset bool `json:"unset" bson:"-"` // Whether the provider is unset + any `collection:"llm_providers"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` // Provider key (e.g., "openai", "anthropic", "gemini") + Name string `json:"name" bson:"name"` // Display name for UI + Enabled bool `json:"enabled" bson:"enabled"` // Whether this provider is enabled + ApiKey string `json:"api_key" bson:"api_key"` // API key for the provider + ApiBaseUrl string `json:"api_base_url" bson:"api_base_url"` // API base URL for the provider + DeploymentName string `json:"deployment_name" bson:"deployment_name"` // Deployment name for the provider + ApiVersion string `json:"api_version" bson:"api_version"` // API version for the provider + Models []string `json:"models" bson:"models"` // Models supported by this provider + Unset bool `json:"unset" bson:"-"` // Whether the provider is unset } func (p *LLMProvider) IsUnset() bool { diff --git a/core/models/models/metric.go b/core/models/models/metric.go index 8f72eb45..a9494754 100644 --- a/core/models/models/metric.go +++ b/core/models/models/metric.go @@ -6,7 +6,7 @@ import ( type Metric struct { any `collection:"metrics"` - BaseModel[Metric] `bson:",inline"` + BaseModel `bson:",inline"` Type string `json:"type" bson:"type"` NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` CpuUsagePercent float32 `json:"cpu_usage_percent" bson:"cpu_usage_percent"` diff --git a/core/models/models/node.go b/core/models/models/node.go index b132fa06..2adcdb23 100644 --- a/core/models/models/node.go +++ b/core/models/models/node.go @@ -5,19 +5,19 @@ import ( ) type Node struct { - any `collection:"nodes"` - BaseModel[Node] `bson:",inline"` - Key string `json:"key" bson:"key"` - Name string `json:"name" bson:"name"` - Ip string `json:"ip" bson:"ip"` - Mac string `json:"mac" bson:"mac"` - Hostname string `json:"hostname" bson:"hostname"` - Description string `json:"description" bson:"description"` - IsMaster bool `json:"is_master" bson:"is_master"` - Status string `json:"status" bson:"status"` - Enabled bool `json:"enabled" bson:"enabled"` - Active bool `json:"active" bson:"active"` - ActiveAt time.Time `json:"active_at" bson:"active_ts"` - CurrentRunners int `json:"current_runners" bson:"current_runners"` - MaxRunners int `json:"max_runners" bson:"max_runners"` + any `collection:"nodes"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` + Name string `json:"name" bson:"name"` + Ip string `json:"ip" bson:"ip"` + Mac string `json:"mac" bson:"mac"` + Hostname string `json:"hostname" bson:"hostname"` + Description string `json:"description" bson:"description"` + IsMaster bool `json:"is_master" bson:"is_master"` + Status string `json:"status" bson:"status"` + Enabled bool `json:"enabled" bson:"enabled"` + Active bool `json:"active" bson:"active"` + ActiveAt time.Time `json:"active_at" bson:"active_ts"` + CurrentRunners int `json:"current_runners" bson:"current_runners"` + MaxRunners int `json:"max_runners" bson:"max_runners"` } diff --git a/core/models/models/notification_alert.go b/core/models/models/notification_alert.go index 65004964..0fb4c145 100644 --- a/core/models/models/notification_alert.go +++ b/core/models/models/notification_alert.go @@ -3,17 +3,17 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type NotificationAlert struct { - any `collection:"notification_alerts"` - BaseModel[NotificationAlert] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Enabled bool `json:"enabled" bson:"enabled"` - HasMetricTarget bool `json:"has_metric_target" bson:"has_metric_target"` - MetricTargetId primitive.ObjectID `json:"metric_target_id,omitempty" bson:"metric_target_id,omitempty"` - MetricName string `json:"metric_name" bson:"metric_name"` - Operator string `json:"operator" bson:"operator"` - LastingSeconds int `json:"lasting_seconds" bson:"lasting_seconds"` - TargetValue float32 `json:"target_value" bson:"target_value"` - Level string `json:"level" bson:"level"` - TemplateKey string `json:"template_key,omitempty" bson:"template_key,omitempty"` + any `collection:"notification_alerts"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + Enabled bool `json:"enabled" bson:"enabled"` + HasMetricTarget bool `json:"has_metric_target" bson:"has_metric_target"` + MetricTargetId primitive.ObjectID `json:"metric_target_id,omitempty" bson:"metric_target_id,omitempty"` + MetricName string `json:"metric_name" bson:"metric_name"` + Operator string `json:"operator" bson:"operator"` + LastingSeconds int `json:"lasting_seconds" bson:"lasting_seconds"` + TargetValue float32 `json:"target_value" bson:"target_value"` + Level string `json:"level" bson:"level"` + TemplateKey string `json:"template_key,omitempty" bson:"template_key,omitempty"` } diff --git a/core/models/models/notification_channel.go b/core/models/models/notification_channel.go index d8ad1cbc..972aca09 100644 --- a/core/models/models/notification_channel.go +++ b/core/models/models/notification_channel.go @@ -1,18 +1,18 @@ package models type NotificationChannel struct { - any `collection:"notification_channels"` - BaseModel[NotificationChannel] `bson:",inline"` - Type string `json:"type" bson:"type"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Provider string `json:"provider" bson:"provider"` - SMTPServer string `json:"smtp_server,omitempty" bson:"smtp_server,omitempty"` - SMTPPort int `json:"smtp_port,omitempty" bson:"smtp_port,omitempty"` - SMTPUsername string `json:"smtp_username,omitempty" bson:"smtp_username,omitempty"` - SMTPPassword string `json:"smtp_password,omitempty" bson:"smtp_password,omitempty"` - WebhookUrl string `json:"webhook_url,omitempty" bson:"webhook_url,omitempty"` - TelegramBotToken string `json:"telegram_bot_token,omitempty" bson:"telegram_bot_token,omitempty"` - TelegramChatId string `json:"telegram_chat_id,omitempty" bson:"telegram_chat_id,omitempty"` - GoogleOAuth2Json string `json:"google_oauth2_json,omitempty" bson:"google_oauth2_json,omitempty"` + any `collection:"notification_channels"` + BaseModel `bson:",inline"` + Type string `json:"type" bson:"type"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + Provider string `json:"provider" bson:"provider"` + SMTPServer string `json:"smtp_server,omitempty" bson:"smtp_server,omitempty"` + SMTPPort int `json:"smtp_port,omitempty" bson:"smtp_port,omitempty"` + SMTPUsername string `json:"smtp_username,omitempty" bson:"smtp_username,omitempty"` + SMTPPassword string `json:"smtp_password,omitempty" bson:"smtp_password,omitempty"` + WebhookUrl string `json:"webhook_url,omitempty" bson:"webhook_url,omitempty"` + TelegramBotToken string `json:"telegram_bot_token,omitempty" bson:"telegram_bot_token,omitempty"` + TelegramChatId string `json:"telegram_chat_id,omitempty" bson:"telegram_chat_id,omitempty"` + GoogleOAuth2Json string `json:"google_oauth2_json,omitempty" bson:"google_oauth2_json,omitempty"` } diff --git a/core/models/models/notification_request.go b/core/models/models/notification_request.go index 480cef85..355f05aa 100644 --- a/core/models/models/notification_request.go +++ b/core/models/models/notification_request.go @@ -3,20 +3,20 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type NotificationRequest struct { - any `collection:"notification_requests"` - BaseModel[NotificationRequest] `bson:",inline"` - Status string `json:"status" bson:"status"` - Error string `json:"error,omitempty" bson:"error,omitempty"` - Title string `json:"title" bson:"title"` - Content string `json:"content" bson:"content"` - SenderEmail string `json:"sender_email,omitempty" bson:"sender_email,omitempty"` - SenderName string `json:"sender_name,omitempty" bson:"sender_name,omitempty"` - MailTo []string `json:"mail_to,omitempty" bson:"mail_to,omitempty"` - MailCc []string `json:"mail_cc,omitempty" bson:"mail_cc,omitempty"` - MailBcc []string `json:"mail_bcc,omitempty" bson:"mail_bcc,omitempty"` - SettingId primitive.ObjectID `json:"setting_id" bson:"setting_id"` - ChannelId primitive.ObjectID `json:"channel_id" bson:"channel_id"` - Setting *NotificationSetting `json:"setting,omitempty" bson:"-"` - Channel *NotificationChannel `json:"channel,omitempty" bson:"-"` - Test bool `json:"test,omitempty" bson:"test,omitempty"` + any `collection:"notification_requests"` + BaseModel `bson:",inline"` + Status string `json:"status" bson:"status"` + Error string `json:"error,omitempty" bson:"error,omitempty"` + Title string `json:"title" bson:"title"` + Content string `json:"content" bson:"content"` + SenderEmail string `json:"sender_email,omitempty" bson:"sender_email,omitempty"` + SenderName string `json:"sender_name,omitempty" bson:"sender_name,omitempty"` + MailTo []string `json:"mail_to,omitempty" bson:"mail_to,omitempty"` + MailCc []string `json:"mail_cc,omitempty" bson:"mail_cc,omitempty"` + MailBcc []string `json:"mail_bcc,omitempty" bson:"mail_bcc,omitempty"` + SettingId primitive.ObjectID `json:"setting_id" bson:"setting_id"` + ChannelId primitive.ObjectID `json:"channel_id" bson:"channel_id"` + Setting *NotificationSetting `json:"setting,omitempty" bson:"-"` + Channel *NotificationChannel `json:"channel,omitempty" bson:"-"` + Test bool `json:"test,omitempty" bson:"test,omitempty"` } diff --git a/core/models/models/notification_setting.go b/core/models/models/notification_setting.go index 80826bec..b937c446 100644 --- a/core/models/models/notification_setting.go +++ b/core/models/models/notification_setting.go @@ -3,11 +3,11 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type NotificationSetting struct { - any `collection:"notification_settings"` - BaseModel[NotificationSetting] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Enabled bool `json:"enabled" bson:"enabled"` + any `collection:"notification_settings"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + Enabled bool `json:"enabled" bson:"enabled"` Title string `json:"title,omitempty" bson:"title,omitempty"` Template string `json:"template" bson:"template"` diff --git a/core/models/models/permission.go b/core/models/models/permission.go index 90882835..e59007d2 100644 --- a/core/models/models/permission.go +++ b/core/models/models/permission.go @@ -3,12 +3,12 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type Permission struct { - any `collection:"permissions"` - BaseModel[Permission] `bson:",inline"` - Key string `json:"key" bson:"key"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` - Type string `json:"type" bson:"type"` - Routes []string `json:"routes" bson:"routes"` + any `collection:"permissions"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` + Type string `json:"type" bson:"type"` + Routes []string `json:"routes" bson:"routes"` } diff --git a/core/models/models/project.go b/core/models/models/project.go index e487ef7b..e3f6a1ca 100644 --- a/core/models/models/project.go +++ b/core/models/models/project.go @@ -1,9 +1,9 @@ package models type Project struct { - any `collection:"projects"` - BaseModel[Project] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Spiders int `json:"spiders" bson:"-"` + any `collection:"projects"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + Spiders int `json:"spiders" bson:"-"` } diff --git a/core/models/models/role.go b/core/models/models/role.go index 2a132f69..ea6b39e9 100644 --- a/core/models/models/role.go +++ b/core/models/models/role.go @@ -1,12 +1,12 @@ package models type Role struct { - any `collection:"roles"` - BaseModel[Role] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - Routes []string `json:"routes" bson:"routes"` - RootAdmin bool `json:"-" bson:"root_admin,omitempty"` - IsRootAdmin bool `json:"root_admin" bson:"-"` - Users int `json:"users" bson:"-"` + any `collection:"roles"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + Routes []string `json:"routes" bson:"routes"` + RootAdmin bool `json:"-" bson:"root_admin,omitempty"` + IsRootAdmin bool `json:"root_admin" bson:"-"` + Users int `json:"users" bson:"-"` } diff --git a/core/models/models/schedule.go b/core/models/models/schedule.go index 22df35da..b4bc232b 100644 --- a/core/models/models/schedule.go +++ b/core/models/models/schedule.go @@ -6,17 +6,17 @@ import ( ) type Schedule struct { - any `collection:"schedules"` - BaseModel[Schedule] `bson:",inline"` - Name string `json:"name" bson:"name"` - Description string `json:"description" bson:"description"` - SpiderId primitive.ObjectID `json:"spider_id" bson:"spider_id"` - Cron string `json:"cron" bson:"cron"` - EntryId cron.EntryID `json:"entry_id" bson:"entry_id"` - Cmd string `json:"cmd" bson:"cmd"` - Param string `json:"param" bson:"param"` - Mode string `json:"mode" bson:"mode"` - NodeIds []primitive.ObjectID `json:"node_ids" bson:"node_ids"` - Priority int `json:"priority" bson:"priority"` - Enabled bool `json:"enabled" bson:"enabled"` + any `collection:"schedules"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Description string `json:"description" bson:"description"` + SpiderId primitive.ObjectID `json:"spider_id" bson:"spider_id"` + Cron string `json:"cron" bson:"cron"` + EntryId cron.EntryID `json:"entry_id" bson:"entry_id"` + Cmd string `json:"cmd" bson:"cmd"` + Param string `json:"param" bson:"param"` + Mode string `json:"mode" bson:"mode"` + NodeIds []primitive.ObjectID `json:"node_ids" bson:"node_ids"` + Priority int `json:"priority" bson:"priority"` + Enabled bool `json:"enabled" bson:"enabled"` } diff --git a/core/models/models/setting.go b/core/models/models/setting.go index 6f61f1c3..c66bdbcd 100644 --- a/core/models/models/setting.go +++ b/core/models/models/setting.go @@ -5,8 +5,8 @@ import ( ) type Setting struct { - any `collection:"settings"` - BaseModel[Setting] `bson:",inline"` - Key string `json:"key" bson:"key"` - Value bson.M `json:"value" bson:"value"` + any `collection:"settings"` + BaseModel `bson:",inline"` + Key string `json:"key" bson:"key"` + Value bson.M `json:"value" bson:"value"` } diff --git a/core/models/models/spider.go b/core/models/models/spider.go index 916a6f53..14b008cd 100644 --- a/core/models/models/spider.go +++ b/core/models/models/spider.go @@ -5,23 +5,23 @@ import ( ) type Spider struct { - any `collection:"spiders"` - BaseModel[Spider] `bson:",inline"` - Name string `json:"name" bson:"name"` // spider name - ColId primitive.ObjectID `json:"col_id" bson:"col_id"` // data collection id (deprecated) # TODO: remove this field in the future - ColName string `json:"col_name,omitempty" bson:"col_name"` // data collection name - DbName string `json:"db_name,omitempty" bson:"db_name"` // database name - DataSourceId primitive.ObjectID `json:"data_source_id" bson:"data_source_id"` // data source id - DataSource *Database `json:"data_source,omitempty" bson:"-"` // data source - Description string `json:"description" bson:"description"` // description - ProjectId primitive.ObjectID `json:"project_id" bson:"project_id"` // Project.Id - Mode string `json:"mode" bson:"mode"` // default Task.Mode - NodeIds []primitive.ObjectID `json:"node_ids" bson:"node_ids"` // default Task.NodeIds - GitId primitive.ObjectID `json:"git_id" bson:"git_id"` // related Git.Id - GitRootPath string `json:"git_root_path" bson:"git_root_path"` - Git *Git `json:"git,omitempty" bson:"-"` - Template string `json:"template,omitempty" bson:"template,omitempty"` // spider template - TemplateParams *SpiderTemplateParams `json:"template_params,omitempty" bson:"template_params,omitempty"` + any `collection:"spiders"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` // spider name + ColId primitive.ObjectID `json:"col_id" bson:"col_id"` // data collection id (deprecated) # TODO: remove this field in the future + ColName string `json:"col_name,omitempty" bson:"col_name"` // data collection name + DbName string `json:"db_name,omitempty" bson:"db_name"` // database name + DataSourceId primitive.ObjectID `json:"data_source_id" bson:"data_source_id"` // data source id + DataSource *Database `json:"data_source,omitempty" bson:"-"` // data source + Description string `json:"description" bson:"description"` // description + ProjectId primitive.ObjectID `json:"project_id" bson:"project_id"` // Project.Id + Mode string `json:"mode" bson:"mode"` // default Task.Mode + NodeIds []primitive.ObjectID `json:"node_ids" bson:"node_ids"` // default Task.NodeIds + GitId primitive.ObjectID `json:"git_id" bson:"git_id"` // related Git.Id + GitRootPath string `json:"git_root_path" bson:"git_root_path"` + Git *Git `json:"git,omitempty" bson:"-"` + Template string `json:"template,omitempty" bson:"template,omitempty"` // spider template + TemplateParams *SpiderTemplateParams `json:"template_params,omitempty" bson:"template_params,omitempty"` // stats Stat *SpiderStat `json:"stat,omitempty" bson:"-"` diff --git a/core/models/models/spider_stat.go b/core/models/models/spider_stat.go index a3c9f631..c122a540 100644 --- a/core/models/models/spider_stat.go +++ b/core/models/models/spider_stat.go @@ -6,7 +6,7 @@ import ( type SpiderStat struct { any `collection:"spider_stats"` - BaseModel[SpiderStat] `bson:",inline"` + BaseModel `bson:",inline"` LastTaskId primitive.ObjectID `json:"last_task_id" bson:"last_task_id,omitempty"` LastTask *Task `json:"last_task,omitempty" bson:"-"` Tasks int `json:"tasks" bson:"tasks"` diff --git a/core/models/models/task.go b/core/models/models/task.go index 69b9c78b..00e723c2 100644 --- a/core/models/models/task.go +++ b/core/models/models/task.go @@ -5,23 +5,23 @@ import ( ) type Task struct { - any `collection:"tasks"` - BaseModel[Task] `bson:",inline"` - SpiderId primitive.ObjectID `json:"spider_id" bson:"spider_id"` - Status string `json:"status" bson:"status"` - NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` - Cmd string `json:"cmd" bson:"cmd"` - Param string `json:"param" bson:"param"` - Error string `json:"error" bson:"error"` - Pid int `json:"pid" bson:"pid"` - ScheduleId primitive.ObjectID `json:"schedule_id" bson:"schedule_id"` - Type string `json:"type" bson:"type"` - Mode string `json:"mode" bson:"mode"` - Priority int `json:"priority" bson:"priority"` - NodeIds []primitive.ObjectID `json:"node_ids,omitempty" bson:"-"` - Stat *TaskStat `json:"stat,omitempty" bson:"-"` - Spider *Spider `json:"spider,omitempty" bson:"-"` - Schedule *Schedule `json:"schedule,omitempty" bson:"-"` - Node *Node `json:"node,omitempty" bson:"-"` - UserId primitive.ObjectID `json:"-" bson:"-"` + any `collection:"tasks"` + BaseModel `bson:",inline"` + SpiderId primitive.ObjectID `json:"spider_id" bson:"spider_id"` + Status string `json:"status" bson:"status"` + NodeId primitive.ObjectID `json:"node_id" bson:"node_id"` + Cmd string `json:"cmd" bson:"cmd"` + Param string `json:"param" bson:"param"` + Error string `json:"error" bson:"error"` + Pid int `json:"pid" bson:"pid"` + ScheduleId primitive.ObjectID `json:"schedule_id" bson:"schedule_id"` + Type string `json:"type" bson:"type"` + Mode string `json:"mode" bson:"mode"` + Priority int `json:"priority" bson:"priority"` + NodeIds []primitive.ObjectID `json:"node_ids,omitempty" bson:"-"` + Stat *TaskStat `json:"stat,omitempty" bson:"-"` + Spider *Spider `json:"spider,omitempty" bson:"-"` + Schedule *Schedule `json:"schedule,omitempty" bson:"-"` + Node *Node `json:"node,omitempty" bson:"-"` + UserId primitive.ObjectID `json:"-" bson:"-"` } diff --git a/core/models/models/task_stat.go b/core/models/models/task_stat.go index 900e8c24..4be7b233 100644 --- a/core/models/models/task_stat.go +++ b/core/models/models/task_stat.go @@ -5,12 +5,12 @@ import ( ) type TaskStat struct { - any `collection:"task_stats"` - BaseModel[TaskStat] `bson:",inline"` - StartTs time.Time `json:"start_ts" bson:"start_ts,omitempty"` - EndTs time.Time `json:"end_ts" bson:"end_ts,omitempty"` - WaitDuration int64 `json:"wait_duration" bson:"wait_duration,omitempty"` // in millisecond - RuntimeDuration int64 `json:"runtime_duration" bson:"runtime_duration,omitempty"` // in millisecond - TotalDuration int64 `json:"total_duration" bson:"total_duration,omitempty"` // in millisecond - ResultCount int64 `json:"result_count" bson:"result_count"` + any `collection:"task_stats"` + BaseModel `bson:",inline"` + StartTs time.Time `json:"start_ts" bson:"start_ts,omitempty"` + EndTs time.Time `json:"end_ts" bson:"end_ts,omitempty"` + WaitDuration int64 `json:"wait_duration" bson:"wait_duration,omitempty"` // in millisecond + RuntimeDuration int64 `json:"runtime_duration" bson:"runtime_duration,omitempty"` // in millisecond + TotalDuration int64 `json:"total_duration" bson:"total_duration,omitempty"` // in millisecond + ResultCount int64 `json:"result_count" bson:"result_count"` } diff --git a/core/models/models/test.go b/core/models/models/test.go index bd31b833..c5f42e8f 100644 --- a/core/models/models/test.go +++ b/core/models/models/test.go @@ -1,7 +1,7 @@ package models type TestModel struct { - any `collection:"testmodels"` - BaseModel[TestModel] `bson:",inline"` - Name string `json:"name" bson:"name"` + any `collection:"testmodels"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` } diff --git a/core/models/models/token.go b/core/models/models/token.go index 5657777c..f421aa2d 100644 --- a/core/models/models/token.go +++ b/core/models/models/token.go @@ -1,8 +1,8 @@ package models type Token struct { - any `collection:"tokens"` - BaseModel[Token] `bson:",inline"` - Name string `json:"name" bson:"name"` - Token string `json:"token" bson:"token"` + any `collection:"tokens"` + BaseModel `bson:",inline"` + Name string `json:"name" bson:"name"` + Token string `json:"token" bson:"token"` } diff --git a/core/models/models/user.go b/core/models/models/user.go index c96bcf6e..f9681cfc 100644 --- a/core/models/models/user.go +++ b/core/models/models/user.go @@ -3,16 +3,16 @@ package models import "go.mongodb.org/mongo-driver/bson/primitive" type User struct { - any `collection:"users"` - BaseModel[User] `bson:",inline"` - Username string `json:"username" bson:"username"` - Password string `json:"-" bson:"password"` - Role string `json:"role" bson:"role"` - RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` - FirstName string `json:"first_name" bson:"first_name"` - LastName string `json:"last_name" bson:"last_name"` - Email string `json:"email" bson:"email"` - RootAdmin bool `json:"root_admin,omitempty" bson:"root_admin"` - RootAdminRole bool `json:"root_admin_role,omitempty" bson:"-"` - Routes []string `json:"routes,omitempty" bson:"-"` + any `collection:"users"` + BaseModel `bson:",inline"` + Username string `json:"username" bson:"username"` + Password string `json:"-" bson:"password"` + Role string `json:"role" bson:"role"` + RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` + FirstName string `json:"first_name" bson:"first_name"` + LastName string `json:"last_name" bson:"last_name"` + Email string `json:"email" bson:"email"` + RootAdmin bool `json:"root_admin,omitempty" bson:"root_admin"` + RootAdminRole bool `json:"root_admin_role,omitempty" bson:"-"` + Routes []string `json:"routes,omitempty" bson:"-"` } diff --git a/core/models/models/user_role.go b/core/models/models/user_role.go index ca54f520..976d5a57 100644 --- a/core/models/models/user_role.go +++ b/core/models/models/user_role.go @@ -5,8 +5,8 @@ import ( ) type UserRole struct { - any `collection:"user_roles"` - BaseModel[UserRole] `bson:",inline"` - RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` - UserId primitive.ObjectID `json:"user_id" bson:"user_id"` + any `collection:"user_roles"` + BaseModel `bson:",inline"` + RoleId primitive.ObjectID `json:"role_id" bson:"role_id"` + UserId primitive.ObjectID `json:"user_id" bson:"user_id"` } diff --git a/core/models/service/base_service_test.go b/core/models/service/base_service_test.go index b9743370..e876587d 100644 --- a/core/models/service/base_service_test.go +++ b/core/models/service/base_service_test.go @@ -17,9 +17,9 @@ import ( ) type TestModel struct { - Id primitive.ObjectID `bson:"_id,omitempty" collection:"testmodels"` - models.BaseModel[TestModel] `bson:",inline"` - Name string `bson:"name"` + Id primitive.ObjectID `bson:"_id,omitempty" collection:"testmodels"` + models.BaseModel `bson:",inline"` + Name string `bson:"name"` } func setupTestDB() { diff --git a/core/openapi/wrapper.go b/core/openapi/wrapper.go index e8259ba9..8f8214a1 100644 --- a/core/openapi/wrapper.go +++ b/core/openapi/wrapper.go @@ -2,7 +2,8 @@ package openapi import ( "fmt" - + "github.com/crawlab-team/crawlab/core/interfaces" + "github.com/crawlab-team/crawlab/core/utils" "github.com/loopfz/gadgeto/tonic" "github.com/gin-gonic/gin" @@ -11,8 +12,9 @@ import ( // FizzWrapper wraps an existing Gin Engine to add OpenAPI functionality type FizzWrapper struct { - fizz *fizz.Fizz - gin *gin.Engine + fizz *fizz.Fizz + gin *gin.Engine + logger interfaces.Logger } // NewFizzWrapper creates a new wrapper around an existing Gin Engine @@ -20,10 +22,10 @@ type FizzWrapper struct { func NewFizzWrapper(engine *gin.Engine) *FizzWrapper { // Create a new Fizz instance using the existing Gin engine f := fizz.NewFromEngine(engine) - return &FizzWrapper{ - fizz: f, - gin: engine, + fizz: f, + gin: engine, + logger: utils.NewLogger("FizzWrapper"), } } diff --git a/go.work b/go.work index ea0e5963..900ac09c 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.23 +go 1.23.7 use ( backend diff --git a/go.work.sum b/go.work.sum index 001314c3..9c0840c0 100644 --- a/go.work.sum +++ b/go.work.sum @@ -973,8 +973,6 @@ github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0 github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 h1:hJix6idebFclqlfZCHE7EUX7uqLCyb70nHNHH1XKGBg= -github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= -github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 h1:Pp8RxiF4rSoXP9SED26WCfNB28/dwTDpPXS8XMJR8rc= github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= @@ -1086,6 +1084,7 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE= github.com/tj/go-buffer v1.1.0 h1:Lo2OsPHlIxXF24zApe15AbK3bJLAOvkkxEA6Ux4c47M= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2 h1:eGaGNxrtoZf/mBURsnNQKDR7u50Klgcf2eFDQEnc8Bc= @@ -1172,6 +1171,7 @@ golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZP golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= @@ -1202,6 +1202,7 @@ golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= @@ -1236,12 +1237,15 @@ golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=