Personal finance tracker
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

init backend

+826 -1
+6 -1
.gitignore
··· 51 51 .ruta 52 52 53 53 # Nix flakes 54 - .direnv 54 + .direnv 55 + 56 + .env 57 + .env.testing 58 + tmp 59 + *.db
+1
backend/.env.example
··· 1 + PORT=
+55
backend/go.mod
··· 1 + module tangled.org/jeffydc.xyz/subete/backend 2 + 3 + go 1.25 4 + 5 + require ( 6 + github.com/danielgtaylor/huma/v2 v2.35.0 7 + github.com/go-chi/chi/v5 v5.2.5 8 + github.com/go-playground/validator/v10 v10.30.1 9 + github.com/spf13/cobra v1.9.1 10 + github.com/spf13/viper v1.21.0 11 + github.com/uptrace/bun v1.2.16 12 + github.com/uptrace/bun/dialect/sqlitedialect v1.2.16 13 + github.com/uptrace/bun/driver/sqliteshim v1.2.16 14 + github.com/uptrace/bun/extra/bundebug v1.2.16 15 + ) 16 + 17 + require ( 18 + github.com/dustin/go-humanize v1.0.1 // indirect 19 + github.com/fatih/color v1.18.0 // indirect 20 + github.com/fsnotify/fsnotify v1.9.0 // indirect 21 + github.com/gabriel-vasile/mimetype v1.4.12 // indirect 22 + github.com/go-playground/locales v0.14.1 // indirect 23 + github.com/go-playground/universal-translator v0.18.1 // indirect 24 + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect 25 + github.com/google/uuid v1.6.0 // indirect 26 + github.com/inconshreveable/mousetrap v1.1.0 // indirect 27 + github.com/jinzhu/inflection v1.0.0 // indirect 28 + github.com/leodido/go-urn v1.4.0 // indirect 29 + github.com/mattn/go-colorable v0.1.14 // indirect 30 + github.com/mattn/go-isatty v0.0.20 // indirect 31 + github.com/mattn/go-sqlite3 v1.14.32 // indirect 32 + github.com/ncruces/go-strftime v1.0.0 // indirect 33 + github.com/pelletier/go-toml/v2 v2.2.4 // indirect 34 + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect 35 + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 36 + github.com/sagikazarmark/locafero v0.11.0 // indirect 37 + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect 38 + github.com/spf13/afero v1.15.0 // indirect 39 + github.com/spf13/cast v1.10.0 // indirect 40 + github.com/spf13/pflag v1.0.10 // indirect 41 + github.com/subosito/gotenv v1.6.0 // indirect 42 + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect 43 + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 44 + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 45 + go.yaml.in/yaml/v3 v3.0.4 // indirect 46 + golang.org/x/crypto v0.48.0 // indirect 47 + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect 48 + golang.org/x/net v0.50.0 // indirect 49 + golang.org/x/sys v0.41.0 // indirect 50 + golang.org/x/text v0.34.0 // indirect 51 + modernc.org/libc v1.67.0 // indirect 52 + modernc.org/mathutil v1.7.1 // indirect 53 + modernc.org/memory v1.11.0 // indirect 54 + modernc.org/sqlite v1.40.1 // indirect 55 + )
+151
backend/go.sum
··· 1 + github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 2 + github.com/danielgtaylor/huma/v2 v2.35.0 h1:FRg3FgVKcMogVhbNY7FjyTwk+p/orLBR3hQBvXXg7dw= 3 + github.com/danielgtaylor/huma/v2 v2.35.0/go.mod h1:3elp5brzdyyZsPlDVvf6w8RLnklKp3abolr+5op3fP0= 4 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 + github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 7 + github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 8 + github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 9 + github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 10 + github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 11 + github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 12 + github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= 13 + github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 14 + github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= 15 + github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= 16 + github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= 17 + github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= 18 + github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 19 + github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 20 + github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 21 + github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 22 + github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 23 + github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 24 + github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= 25 + github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= 26 + github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= 27 + github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 28 + github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 29 + github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 30 + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= 31 + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 32 + github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 33 + github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 34 + github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 35 + github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 36 + github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 37 + github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 38 + github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 39 + github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 40 + github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 41 + github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 42 + github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 43 + github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 44 + github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 45 + github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 46 + github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 47 + github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 48 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 49 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 50 + github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= 51 + github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 52 + github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= 53 + github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= 54 + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 55 + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 56 + github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= 57 + github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 58 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 59 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 60 + github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= 61 + github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 62 + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 63 + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 64 + github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 65 + github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 66 + github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 67 + github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= 68 + github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= 69 + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= 70 + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= 71 + github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= 72 + github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= 73 + github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= 74 + github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 75 + github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 76 + github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 77 + github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 78 + github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 79 + github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 80 + github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= 81 + github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= 82 + github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 83 + github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 84 + github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 85 + github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 86 + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= 87 + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= 88 + github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= 89 + github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM= 90 + github.com/uptrace/bun/dialect/sqlitedialect v1.2.16 h1:6wVAiYLj1pMibRthGwy4wDLa3D5AQo32Y8rvwPd8CQ0= 91 + github.com/uptrace/bun/dialect/sqlitedialect v1.2.16/go.mod h1:Z7+5qK8CGZkDQiPMu+LSdVuDuR1I5jcwtkB1Pi3F82E= 92 + github.com/uptrace/bun/driver/sqliteshim v1.2.16 h1:M6Dh5kkDWFbUWBrOsIE1g1zdZ5JbSytTD4piFRBOUAI= 93 + github.com/uptrace/bun/driver/sqliteshim v1.2.16/go.mod h1:iKdJ06P3XS+pwKcONjSIK07bbhksH3lWsw3mpfr0+bY= 94 + github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0= 95 + github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo= 96 + github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= 97 + github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= 98 + github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 99 + github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 100 + go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 101 + go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 102 + golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= 103 + golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= 104 + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= 105 + golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= 106 + golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= 107 + golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= 108 + golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= 109 + golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= 110 + golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= 111 + golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 112 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 113 + golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= 114 + golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 115 + golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= 116 + golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 117 + golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= 118 + golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= 119 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 120 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 121 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 122 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 123 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 124 + modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= 125 + modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= 126 + modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= 127 + modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM= 128 + modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= 129 + modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= 130 + modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= 131 + modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= 132 + modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE= 133 + modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= 134 + modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= 135 + modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= 136 + modernc.org/libc v1.67.0 h1:QzL4IrKab2OFmxA3/vRYl0tLXrIamwrhD6CKD4WBVjQ= 137 + modernc.org/libc v1.67.0/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA= 138 + modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= 139 + modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= 140 + modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= 141 + modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= 142 + modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= 143 + modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= 144 + modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= 145 + modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= 146 + modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY= 147 + modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= 148 + modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= 149 + modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= 150 + modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 151 + modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+45
backend/internal/config/config.go
··· 1 + package config 2 + 3 + import ( 4 + "os" 5 + 6 + "github.com/go-playground/validator/v10" 7 + "github.com/spf13/viper" 8 + ) 9 + 10 + type Config struct { 11 + APP_ENV string `validate:"required,oneof=development testing production"` 12 + APP_ADDR string `validate:"required,url"` 13 + DB_NAME string `validate:"required"` 14 + } 15 + 16 + func Load() *Config { 17 + env := os.Getenv("APP_ENV") 18 + 19 + v := viper.New() 20 + v.SetConfigFile(".env") 21 + v.SetConfigType("env") 22 + 23 + if err := v.ReadInConfig(); err != nil { 24 + panic(err) 25 + } 26 + 27 + if env == "testing" { 28 + v.SetConfigFile(".env.testing") 29 + if err := v.MergeInConfig(); err != nil { 30 + panic(err) 31 + } 32 + } 33 + 34 + var config Config 35 + if err := v.Unmarshal(&config); err != nil { 36 + panic(err) 37 + } 38 + 39 + validate := validator.New(validator.WithRequiredStructEnabled()) 40 + if err := validate.Struct(config); err != nil { 41 + panic(err) 42 + } 43 + 44 + return &config 45 + }
+134
backend/internal/db/db.go
··· 1 + package db 2 + 3 + import ( 4 + "database/sql" 5 + "fmt" 6 + "strings" 7 + 8 + "github.com/danielgtaylor/huma/v2/humacli" 9 + "github.com/spf13/cobra" 10 + "github.com/uptrace/bun" 11 + "github.com/uptrace/bun/dialect/sqlitedialect" 12 + "github.com/uptrace/bun/driver/sqliteshim" 13 + "github.com/uptrace/bun/migrate" 14 + 15 + "tangled.org/jeffydc.xyz/subete/backend/internal/config" 16 + "tangled.org/jeffydc.xyz/subete/backend/internal/db/migrations" 17 + ) 18 + 19 + func NewDB(cfg *config.Config) *bun.DB { 20 + sqldb, err := sql.Open(sqliteshim.ShimName, cfg.DB_NAME) 21 + if err != nil { 22 + panic(err) 23 + } 24 + return bun.NewDB(sqldb, sqlitedialect.New()) 25 + } 26 + 27 + func newMigrator() *migrate.Migrator { 28 + return migrate.NewMigrator(NewDB(config.Load()), migrations.Migrations) 29 + } 30 + 31 + func AddDBCommands(cli humacli.CLI) { 32 + dbCmd := &cobra.Command{ 33 + Use: "db", 34 + Short: "Database management commands", 35 + } 36 + 37 + dbCmd.AddCommand( 38 + &cobra.Command{ 39 + Use: "init", 40 + Short: "Create bun_migrations table", 41 + RunE: func(cmd *cobra.Command, args []string) error { 42 + return newMigrator().Init(cmd.Context()) 43 + }, 44 + }, 45 + &cobra.Command{ 46 + Use: "migrate", 47 + Short: "Run pending migrations", 48 + RunE: func(cmd *cobra.Command, args []string) error { 49 + m := newMigrator() 50 + if err := m.Lock(cmd.Context()); err != nil { 51 + return err 52 + } 53 + defer m.Unlock(cmd.Context()) 54 + 55 + group, err := m.Migrate(cmd.Context()) 56 + if err != nil { 57 + return err 58 + } 59 + if group.IsZero() { 60 + fmt.Println("there are no new migrations to run (database is up to date)") 61 + return nil 62 + } 63 + fmt.Printf("migrated to %s\n", group) 64 + return nil 65 + }, 66 + }, 67 + &cobra.Command{ 68 + Use: "migrate:rollback", 69 + Short: "Rollback the last migration group", 70 + RunE: func(cmd *cobra.Command, args []string) error { 71 + m := newMigrator() 72 + if err := m.Lock(cmd.Context()); err != nil { 73 + return err 74 + } 75 + defer m.Unlock(cmd.Context()) 76 + 77 + group, err := m.Rollback(cmd.Context()) 78 + if err != nil { 79 + return err 80 + } 81 + if group.IsZero() { 82 + fmt.Println("there are no groups to roll back") 83 + return nil 84 + } 85 + fmt.Printf("rolled back %s\n", group) 86 + return nil 87 + }, 88 + }, 89 + &cobra.Command{ 90 + Use: "migrate:new [name]", 91 + Short: "Create a new Go migration file", 92 + Args: cobra.MinimumNArgs(1), 93 + RunE: func(cmd *cobra.Command, args []string) error { 94 + name := strings.Join(args, "_") 95 + mf, err := newMigrator().CreateGoMigration(cmd.Context(), name) 96 + if err != nil { 97 + return err 98 + } 99 + fmt.Printf("created migration %s (%s)\n", mf.Name, mf.Path) 100 + return nil 101 + }, 102 + }, 103 + &cobra.Command{ 104 + Use: "lock", 105 + Short: "Lock migrations", 106 + RunE: func(cmd *cobra.Command, args []string) error { 107 + return newMigrator().Lock(cmd.Context()) 108 + }, 109 + }, 110 + &cobra.Command{ 111 + Use: "unlock", 112 + Short: "Unlock migrations", 113 + RunE: func(cmd *cobra.Command, args []string) error { 114 + return newMigrator().Unlock(cmd.Context()) 115 + }, 116 + }, 117 + &cobra.Command{ 118 + Use: "status", 119 + Short: "Print migrations status", 120 + RunE: func(cmd *cobra.Command, args []string) error { 121 + ms, err := newMigrator().MigrationsWithStatus(cmd.Context()) 122 + if err != nil { 123 + return err 124 + } 125 + fmt.Printf("migrations: %s\n", ms) 126 + fmt.Printf("unapplied migrations: %s\n", ms.Unapplied()) 127 + fmt.Printf("last migration group: %s\n", ms.LastGroup()) 128 + return nil 129 + }, 130 + }, 131 + ) 132 + 133 + cli.Root().AddCommand(dbCmd) 134 + }
+33
backend/internal/db/migrations/20260215122556_init_db.go
··· 1 + package migrations 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + 7 + "github.com/uptrace/bun" 8 + "tangled.org/jeffydc.xyz/subete/backend/user" 9 + "tangled.org/jeffydc.xyz/subete/backend/wallet" 10 + ) 11 + 12 + func init() { 13 + Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error { 14 + fmt.Print(" [up migration] ") 15 + initDB(ctx, db) 16 + return nil 17 + }, func(ctx context.Context, db *bun.DB) error { 18 + fmt.Print(" [down migration] ") 19 + initDB(ctx, db) 20 + return nil 21 + }) 22 + } 23 + 24 + func initDB(ctx context.Context, db *bun.DB) { 25 + err := db.ResetModel(ctx, 26 + (*user.UserModel)(nil), 27 + (*wallet.WalletModel)(nil), 28 + ) 29 + 30 + if err != nil { 31 + panic(err) 32 + } 33 + }
+5
backend/internal/db/migrations/main.go
··· 1 + package migrations 2 + 3 + import "github.com/uptrace/bun/migrate" 4 + 5 + var Migrations = migrate.NewMigrations()
+10
backend/internal/utils/utils.go
··· 1 + package utils 2 + 3 + import "time" 4 + 5 + type BaseModelSchema struct { 6 + ID uint64 `bun:"id,pk,autoincrement" json:"id"` 7 + CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp" json:"createdAt"` 8 + UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp" json:"updatedAt"` 9 + DeletedAt time.Time `bun:"deleted_at,soft_delete,nullzero" json:"deletedAt"` 10 + }
+68
backend/main.go
··· 1 + package main 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "net/http" 7 + "time" 8 + 9 + "github.com/danielgtaylor/huma/v2" 10 + "github.com/danielgtaylor/huma/v2/adapters/humachi" 11 + "github.com/danielgtaylor/huma/v2/humacli" 12 + "github.com/go-chi/chi/v5" 13 + "github.com/go-chi/chi/v5/middleware" 14 + "github.com/uptrace/bun/extra/bundebug" 15 + 16 + "tangled.org/jeffydc.xyz/subete/backend/internal/config" 17 + "tangled.org/jeffydc.xyz/subete/backend/internal/db" 18 + "tangled.org/jeffydc.xyz/subete/backend/wallet" 19 + ) 20 + 21 + type Options struct{} 22 + 23 + func main() { 24 + cli := humacli.New(func(hooks humacli.Hooks, options *Options) { 25 + // Application config 26 + config := config.Load() 27 + 28 + // DB 29 + bundb := db.NewDB(config) 30 + bundb.AddQueryHook(bundebug.NewQueryHook( 31 + bundebug.FromEnv(), 32 + )) 33 + 34 + // Router 35 + r := chi.NewRouter() 36 + r.Use(middleware.Logger) 37 + 38 + // API routers 39 + r.Route("/api", func(r chi.Router) { 40 + humaconfig := huma.DefaultConfig("Subete API", "v1") 41 + humaconfig.Servers = []*huma.Server{ 42 + {URL: "/api"}, 43 + } 44 + api := humachi.New(r, humaconfig) 45 + // user.New(config, bundb).Register(api) 46 + wallet.New(config, bundb).Register(api) 47 + }) 48 + 49 + // Server 50 + server := http.Server{ 51 + Addr: config.APP_ADDR, 52 + Handler: r, 53 + } 54 + hooks.OnStart(func() { 55 + fmt.Println("server at:", config.APP_ADDR) 56 + server.ListenAndServe() 57 + }) 58 + hooks.OnStop(func() { 59 + // Give the server 5 seconds to gracefully shut down, then give up. 60 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 61 + defer cancel() 62 + server.Shutdown(ctx) 63 + }) 64 + }) 65 + 66 + db.AddDBCommands(cli) 67 + cli.Run() 68 + }
+62
backend/user/user_model.go
··· 1 + package user 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "strings" 7 + "time" 8 + 9 + "github.com/uptrace/bun" 10 + ) 11 + 12 + type UserModel struct { 13 + bun.BaseModel `bun:"table:users"` 14 + 15 + ID string `bun:"id,pk,autoincrement"` 16 + Username string `bun:"username,unique,notnull"` 17 + Password string `bun:"password,unique,notnull"` 18 + 19 + CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"` 20 + UpdatedAt time.Time `bun:"updated_at,nullzero,notnull,default:current_timestamp"` 21 + DeletedAt time.Time `bun:"deleted_at,soft_delete,nullzero"` 22 + } 23 + 24 + func createUser(ctx context.Context, db *bun.DB, username string, password string) (*UserModel, error) { 25 + user := &UserModel{Username: username, Password: password} 26 + _, err := db.NewInsert().Model(&user).Exec(ctx) 27 + if err != nil { 28 + if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "UNIQUE constraint") { 29 + return nil, fmt.Errorf("username %s is taken.", username) 30 + } 31 + return nil, err 32 + } 33 + 34 + return user, nil 35 + } 36 + 37 + func getUserByID(ctx context.Context, db *bun.DB, id string) (*UserModel, error) { 38 + user := new(UserModel) 39 + err := db.NewSelect().Model(user).Where("id = ?", id).Scan(ctx) 40 + if err != nil { 41 + return nil, fmt.Errorf("user not found") 42 + } 43 + return user, nil 44 + } 45 + 46 + func updateUser(ctx context.Context, db *bun.DB, id string, username string, password string) (*UserModel, error) { 47 + user := &UserModel{ID: id, Username: username, Password: password} 48 + _, err := db.NewUpdate().Model(user).Column("username", "password", "updated_at").Where("id = ?", id).Exec(ctx) 49 + if err != nil { 50 + if strings.Contains(err.Error(), "duplicate key") || strings.Contains(err.Error(), "UNIQUE constraint") { 51 + return nil, fmt.Errorf("username %s is taken.", username) 52 + } 53 + return nil, err 54 + } 55 + return user, nil 56 + } 57 + 58 + func deleteUser(ctx context.Context, db *bun.DB, id string) error { 59 + user := new(UserModel) 60 + _, err := db.NewDelete().Model(user).Where("id = ?", id).Exec(ctx) 61 + return err 62 + }
+82
backend/user/user_router.go
··· 1 + package user 2 + 3 + import ( 4 + "context" 5 + "encoding/json" 6 + "net/http" 7 + 8 + "github.com/danielgtaylor/huma/v2" 9 + "github.com/go-chi/chi/v5" 10 + "github.com/uptrace/bun" 11 + "tangled.org/jeffydc.xyz/subete/backend/internal/config" 12 + ) 13 + 14 + type UserDep struct { 15 + config *config.Config 16 + db *bun.DB 17 + } 18 + 19 + func New(config *config.Config, db *bun.DB) *UserDep { 20 + return &UserDep{ 21 + config: config, 22 + db: db, 23 + } 24 + } 25 + 26 + func (self *UserDep) Register(api huma.API) { 27 + // huma.Get(api, "/me", self.getUser) 28 + r := chi.NewRouter() 29 + // r.Get("/me", self.getUser) 30 + r.Post("/me", self.createUser) 31 + r.Put("/me", self.updateUser) 32 + r.Delete("/me", self.deleteUser) 33 + 34 + // p.Mount("/users", r) 35 + } 36 + 37 + func (self *UserDep) getUser(ctx context.Context) { 38 + // userID := r.Context().Value("userID").(string) 39 + // user, err := getUserByID(r.Context(), self.db, userID) 40 + // if err != nil { 41 + // http.Error(w, err.Error(), http.StatusNotFound) 42 + // return 43 + // } 44 + // w.Header().Set("Content-Type", "application/json") 45 + // json.NewEncoder(w).Encode(user) 46 + } 47 + 48 + func (self *UserDep) createUser(w http.ResponseWriter, r *http.Request) { 49 + username := r.FormValue("username") 50 + password := r.FormValue("password") 51 + user, err := createUser(r.Context(), self.db, username, password) 52 + if err != nil { 53 + http.Error(w, err.Error(), http.StatusBadRequest) 54 + return 55 + } 56 + w.Header().Set("Content-Type", "application/json") 57 + w.WriteHeader(http.StatusCreated) 58 + json.NewEncoder(w).Encode(user) 59 + } 60 + 61 + func (self *UserDep) updateUser(w http.ResponseWriter, r *http.Request) { 62 + userID := r.Context().Value("userID").(string) 63 + username := r.FormValue("username") 64 + password := r.FormValue("password") 65 + user, err := updateUser(r.Context(), self.db, userID, username, password) 66 + if err != nil { 67 + http.Error(w, err.Error(), http.StatusBadRequest) 68 + return 69 + } 70 + w.Header().Set("Content-Type", "application/json") 71 + json.NewEncoder(w).Encode(user) 72 + } 73 + 74 + func (self *UserDep) deleteUser(w http.ResponseWriter, r *http.Request) { 75 + userID := r.Context().Value("userID").(string) 76 + err := deleteUser(r.Context(), self.db, userID) 77 + if err != nil { 78 + http.Error(w, err.Error(), http.StatusBadRequest) 79 + return 80 + } 81 + w.WriteHeader(http.StatusNoContent) 82 + }
+65
backend/wallet/wallet_model.go
··· 1 + package wallet 2 + 3 + import ( 4 + "context" 5 + 6 + "github.com/uptrace/bun" 7 + "tangled.org/jeffydc.xyz/subete/backend/internal/utils" 8 + ) 9 + 10 + type WalletModel struct { 11 + bun.BaseModel `bun:"table:wallets"` 12 + utils.BaseModelSchema 13 + 14 + Name string `bun:"name" json:"name"` 15 + } 16 + 17 + type CreateWalletSchema struct { 18 + Name string `json:"name"` 19 + } 20 + 21 + type updateWalletSchema struct { 22 + *CreateWalletSchema 23 + ID uint64 `json:"id"` 24 + } 25 + 26 + func getWallets(ctx context.Context, db *bun.DB) (*[]WalletModel, error) { 27 + wallets := &[]WalletModel{} 28 + err := db.NewSelect().Model(wallets).Scan(ctx) 29 + if err != nil { 30 + return nil, err 31 + } 32 + return wallets, nil 33 + } 34 + 35 + func getWalletByID(ctx context.Context, db *bun.DB, id uint64) (*WalletModel, error) { 36 + wallet := &WalletModel{} 37 + err := db.NewSelect().Model(wallet).Where("id = ?", id).Scan(ctx) 38 + if err != nil { 39 + return nil, err 40 + } 41 + return wallet, nil 42 + } 43 + 44 + func createWallet(ctx context.Context, db *bun.DB, schema *CreateWalletSchema) (*WalletModel, error) { 45 + wallet := &WalletModel{Name: schema.Name} 46 + _, err := db.NewInsert().Model(wallet).Exec(ctx) 47 + if err != nil { 48 + return nil, err 49 + } 50 + return wallet, nil 51 + } 52 + 53 + func updateWallet(ctx context.Context, db *bun.DB, schema *updateWalletSchema) (*WalletModel, error) { 54 + wallet := &WalletModel{Name: schema.Name} 55 + _, err := db.NewUpdate().Model(wallet).Where("id = ?", schema.ID).Exec(ctx) 56 + if err != nil { 57 + return nil, err 58 + } 59 + return wallet, nil 60 + } 61 + 62 + func deleteWallet(ctx context.Context, db *bun.DB, walletId uint64) error { 63 + _, err := db.NewDelete().Model((*WalletModel)(nil)).Where("id = ?", walletId).Exec(ctx) 64 + return err 65 + }
+103
backend/wallet/wallet_router.go
··· 1 + package wallet 2 + 3 + import ( 4 + "context" 5 + 6 + "github.com/danielgtaylor/huma/v2" 7 + "github.com/uptrace/bun" 8 + "tangled.org/jeffydc.xyz/subete/backend/internal/config" 9 + ) 10 + 11 + type handler struct { 12 + config *config.Config 13 + db *bun.DB 14 + } 15 + 16 + type walletIdInput struct { 17 + ID uint64 `path:"walletId"` 18 + } 19 + 20 + func New(config *config.Config, db *bun.DB) *handler { 21 + return &handler{ 22 + config: config, 23 + db: db, 24 + } 25 + } 26 + 27 + func (self *handler) Register(api huma.API) { 28 + huma.Get(api, "/wallets", self.list) 29 + huma.Post(api, "/wallets", self.create) 30 + huma.Get(api, "/wallet/{walletId}", self.show) 31 + huma.Put(api, "/wallet/{walletId}", self.update) 32 + huma.Delete(api, "/wallet/{walletId}", self.delete) 33 + } 34 + 35 + type walletsResp struct { 36 + Body *[]WalletModel 37 + } 38 + 39 + func (self *handler) list(ctx context.Context, input *struct{}) (*walletsResp, error) { 40 + wallets, err := getWallets(ctx, self.db) 41 + if err != nil { 42 + return nil, huma.Error400BadRequest(err.Error(), err) 43 + } 44 + resp := &walletsResp{} 45 + resp.Body = wallets 46 + return resp, nil 47 + } 48 + 49 + type walletResp struct { 50 + Body *WalletModel 51 + } 52 + 53 + func (self *handler) show(ctx context.Context, input *walletIdInput) (*walletResp, error) { 54 + wallet, err := getWalletByID(ctx, self.db, input.ID) 55 + if err != nil { 56 + return nil, huma.Error404NotFound(err.Error(), err) 57 + } 58 + resp := &walletResp{} 59 + resp.Body = wallet 60 + return resp, nil 61 + } 62 + 63 + type createWalletInput struct { 64 + Body CreateWalletSchema 65 + } 66 + 67 + func (self *handler) create(ctx context.Context, input *createWalletInput) (*walletResp, error) { 68 + wallet, err := createWallet(ctx, self.db, &input.Body) 69 + if err != nil { 70 + return nil, huma.Error400BadRequest(err.Error(), err) 71 + } 72 + resp := &walletResp{} 73 + resp.Body = wallet 74 + return resp, nil 75 + } 76 + 77 + type updateWalletInput struct { 78 + Body updateWalletSchema 79 + } 80 + 81 + func (self *handler) update(ctx context.Context, input *updateWalletInput) (*walletResp, error) { 82 + wallet, err := updateWallet(ctx, self.db, &input.Body) 83 + if err != nil { 84 + return nil, huma.Error400BadRequest(err.Error(), err) 85 + } 86 + resp := &walletResp{} 87 + resp.Body = wallet 88 + return resp, nil 89 + } 90 + 91 + type deleteWalletResp struct { 92 + Body string 93 + } 94 + 95 + func (self *handler) delete(ctx context.Context, input *walletIdInput) (*deleteWalletResp, error) { 96 + err := deleteWallet(ctx, self.db, input.ID) 97 + if err != nil { 98 + return nil, huma.Error400BadRequest(err.Error(), err) 99 + } 100 + resp := &deleteWalletResp{} 101 + resp.Body = "ok" 102 + return resp, nil 103 + }
+6
flake.nix
··· 30 30 default = pkgs.mkShellNoCC { 31 31 name = "subete"; 32 32 packages = with pkgs; [ 33 + go 34 + air 35 + gopls 33 36 pnpm 34 37 nodejs_24 35 38 ]; 39 + 40 + CGO_ENABLED = 0; 41 + BUNDEBUG = 2; 36 42 }; 37 43 } 38 44 );