Personal finance tracker
0
fork

Configure Feed

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

wip: transaction route integration

+1027 -474
-4
backend/go.mod
··· 30 30 github.com/mattn/go-colorable v0.1.14 // indirect 31 31 github.com/mattn/go-isatty v0.0.20 // indirect 32 32 github.com/mattn/go-sqlite3 v1.14.34 // indirect 33 - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect 34 33 github.com/mr-tron/base58 v1.2.0 // indirect 35 34 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 36 35 github.com/ncruces/go-strftime v1.0.0 // indirect ··· 41 40 github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect 42 41 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 43 42 github.com/spf13/pflag v1.0.10 // indirect 44 - github.com/stretchr/testify v1.11.1 // indirect 45 43 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect 46 44 github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 47 45 github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect ··· 50 48 go.yaml.in/yaml/v2 v2.4.3 // indirect 51 49 golang.org/x/crypto v0.48.0 // indirect 52 50 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect 53 - golang.org/x/net v0.51.0 // indirect 54 51 golang.org/x/sys v0.41.0 // indirect 55 - golang.org/x/text v0.34.0 // indirect 56 52 golang.org/x/time v0.14.0 // indirect 57 53 google.golang.org/protobuf v1.36.11 // indirect 58 54 modernc.org/libc v1.68.0 // indirect
+20 -61
backend/go.sum
··· 2 2 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 3 3 github.com/bluesky-social/indigo v0.0.0-20260220055544-bf41e2ee75ab h1:Cs35T2tAN3Q6mMH5mBaY09nmCNOn/GkZS1F7jfMxlR8= 4 4 github.com/bluesky-social/indigo v0.0.0-20260220055544-bf41e2ee75ab/go.mod h1:VG/LeqLGNI3Ew7lsYixajnZGFfWPv144qbUddh+Oyag= 5 - github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 6 - github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 7 5 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 8 6 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 9 7 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 10 - github.com/danielgtaylor/huma/v2 v2.35.0 h1:FRg3FgVKcMogVhbNY7FjyTwk+p/orLBR3hQBvXXg7dw= 11 - github.com/danielgtaylor/huma/v2 v2.35.0/go.mod h1:3elp5brzdyyZsPlDVvf6w8RLnklKp3abolr+5op3fP0= 12 8 github.com/danielgtaylor/huma/v2 v2.37.2 h1:Nf9vjy2sxBJFaupPlthXL/Hy2+LurfVbaKHmCMEI7xE= 13 9 github.com/danielgtaylor/huma/v2 v2.37.2/go.mod h1:95S04G/lExFRYlBkKaBaZm9lVmxRmqX9f2CgoOZ11AM= 14 10 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= ··· 19 15 github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw= 20 16 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 21 17 github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 18 + github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= 19 + github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 22 20 github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= 23 21 github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= 24 - github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 25 - github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 26 - github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 27 22 github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= 28 23 github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= 29 - github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 30 24 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 31 25 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 32 26 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 33 - github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 34 - github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 35 27 github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= 36 28 github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= 37 29 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= ··· 44 36 github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= 45 37 github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= 46 38 github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= 47 - github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= 48 39 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 49 40 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 50 41 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= ··· 53 44 github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= 54 45 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 55 46 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 56 - github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= 57 - github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 47 + github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= 48 + github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 49 + github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 50 + github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 51 + github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 52 + github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 58 53 github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 59 54 github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 60 55 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 61 56 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 62 - github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= 63 - github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 64 57 github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= 65 58 github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 66 - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= 67 - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 68 59 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 69 60 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 70 61 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= ··· 85 76 github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= 86 77 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 87 78 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 88 - github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= 89 - github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 90 79 github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= 91 80 github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= 92 - github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= 93 - github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= 94 81 github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 95 82 github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= 96 - github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= 97 - github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= 98 83 github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= 99 84 github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= 100 - github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= 101 - github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 102 85 github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= 103 86 github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= 104 87 github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= 105 88 github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 106 89 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 107 90 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 91 + github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 92 + github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 108 93 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 109 94 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 110 95 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 111 - github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 112 - github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 113 96 github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 114 97 github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 115 - github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 116 98 github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 117 99 github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 118 100 github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= ··· 120 102 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 121 103 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= 122 104 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= 123 - github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= 124 - github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM= 125 105 github.com/uptrace/bun v1.2.17 h1:3AV30/MrgVIL8haNbIQ7Z4I/eQGmaSlfK2T8W8ZprhM= 126 106 github.com/uptrace/bun v1.2.17/go.mod h1:wNltaKJk4JtOt4SG5I5zmA7v0/Mzjh1+/S906Rayd3Y= 127 - github.com/uptrace/bun/dialect/sqlitedialect v1.2.16 h1:6wVAiYLj1pMibRthGwy4wDLa3D5AQo32Y8rvwPd8CQ0= 128 - github.com/uptrace/bun/dialect/sqlitedialect v1.2.16/go.mod h1:Z7+5qK8CGZkDQiPMu+LSdVuDuR1I5jcwtkB1Pi3F82E= 129 107 github.com/uptrace/bun/dialect/sqlitedialect v1.2.17 h1:ZipEoNr+wQJQleGy2poKSSoaQDavzc+nXTDp3ZzkA0E= 130 108 github.com/uptrace/bun/dialect/sqlitedialect v1.2.17/go.mod h1:phXmrxxeYqUhMU09FgazbfNxq9LlArdqjZqHc1ILy9U= 131 - github.com/uptrace/bun/driver/sqliteshim v1.2.16 h1:M6Dh5kkDWFbUWBrOsIE1g1zdZ5JbSytTD4piFRBOUAI= 132 - github.com/uptrace/bun/driver/sqliteshim v1.2.16/go.mod h1:iKdJ06P3XS+pwKcONjSIK07bbhksH3lWsw3mpfr0+bY= 133 109 github.com/uptrace/bun/driver/sqliteshim v1.2.17 h1:0Xa4FZp93D1LCCaMCiPjFsO36b4aQ1vFdXYD7Zk/WM4= 134 110 github.com/uptrace/bun/driver/sqliteshim v1.2.17/go.mod h1:MqvqMCAAKNn6M0HF9YK/Z6xrnCP6sih5OZ37AxdAlHw= 135 - github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0= 136 - github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo= 137 111 github.com/uptrace/bun/extra/bundebug v1.2.17 h1:QQh0d3WgJU0NxDjPbA2GOrvSdfs5Jm1KsAZRRr7KyKM= 138 112 github.com/uptrace/bun/extra/bundebug v1.2.17/go.mod h1:zIN0ah3VkBYt9VKfnQVRSzd7JYKaK4AGyyD39AGwHwg= 139 113 github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= ··· 142 116 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 143 117 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 144 118 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 119 + github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 120 + github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 145 121 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 146 122 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 147 123 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 148 124 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 125 + go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 126 + go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 149 127 go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= 150 128 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= 151 129 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 152 130 golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= 153 131 golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= 154 - golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= 155 - golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= 156 132 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= 157 133 golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= 158 - golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= 159 - golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= 160 134 golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= 161 - golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= 162 - golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= 163 - golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= 164 - golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= 135 + golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= 165 136 golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= 166 137 golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 167 138 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 168 139 golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= 169 140 golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 170 - golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= 171 - golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 172 - golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 173 - golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 174 141 golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= 175 142 golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 176 - golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= 177 - golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= 178 143 golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= 179 - golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 144 + golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= 180 145 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 181 146 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 182 - google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 183 - google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 184 147 google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= 185 148 google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 186 149 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 150 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 151 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 187 152 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 188 153 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 189 154 lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 190 155 lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 191 156 modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= 192 157 modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= 193 - modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= 194 - modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM= 195 158 modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so= 159 + modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw= 196 160 modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= 197 161 modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= 198 162 modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= 199 163 modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= 200 - modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE= 201 - modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= 202 164 modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= 165 + modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= 203 166 modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= 204 167 modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= 205 - modernc.org/libc v1.67.0 h1:QzL4IrKab2OFmxA3/vRYl0tLXrIamwrhD6CKD4WBVjQ= 206 - modernc.org/libc v1.67.0/go.mod h1:QvvnnJ5P7aitu0ReNpVIEyesuhmDLQ8kaEoyMjIFZJA= 207 168 modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= 208 169 modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= 209 170 modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= ··· 214 175 modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= 215 176 modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= 216 177 modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= 217 - modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY= 218 - modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE= 219 178 modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= 220 179 modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= 221 180 modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
+2
backend/internal/db/migrations/20260215122556_init_db.go
··· 6 6 7 7 "github.com/uptrace/bun" 8 8 "tangled.org/jeffydc.xyz/subete/backend/oauth" 9 + "tangled.org/jeffydc.xyz/subete/backend/transaction" 9 10 "tangled.org/jeffydc.xyz/subete/backend/wallet" 10 11 ) 11 12 ··· 27 28 (*oauth.AuthRequestModel)(nil), 28 29 (*oauth.UserModel)(nil), 29 30 (*wallet.WalletModel)(nil), 31 + (*transaction.TransactionModel)(nil), 30 32 ) 31 33 if err != nil { 32 34 panic(err)
+2
backend/main.go
··· 16 16 "tangled.org/jeffydc.xyz/subete/backend/internal/config" 17 17 "tangled.org/jeffydc.xyz/subete/backend/internal/db" 18 18 "tangled.org/jeffydc.xyz/subete/backend/oauth" 19 + "tangled.org/jeffydc.xyz/subete/backend/transaction" 19 20 "tangled.org/jeffydc.xyz/subete/backend/wallet" 20 21 ) 21 22 ··· 48 49 // Subete API routes 49 50 huma.Get(api, "/me", oa.CurrentUser) 50 51 wallet.New(bundb).Register(api) 52 + transaction.New(bundb).Register(api) 51 53 }) 52 54 53 55 // Server
+74
backend/transaction/transaction.go
··· 1 + package transaction 2 + 3 + import ( 4 + "context" 5 + 6 + "github.com/uptrace/bun" 7 + "tangled.org/jeffydc.xyz/subete/backend/internal/utils" 8 + "tangled.org/jeffydc.xyz/subete/backend/wallet" 9 + ) 10 + 11 + type TransactionModel struct { 12 + bun.BaseModel `bun:"table:transactions"` 13 + utils.BaseModelSchema 14 + 15 + Reason string `bun:"reason"` 16 + Amount uint64 `bun:"amount"` 17 + Type string `bun:"type"` 18 + 19 + WalletID uint64 `bun:"wallet_id,notnull"` 20 + Wallet *wallet.WalletModel `bun:"rel:belongs-to,join:wallet_id=id"` 21 + } 22 + 23 + type CreateTransactionSchema struct { 24 + WalletID uint64 `json:"walletID"` 25 + Reason string `json:"reason" minLength:"1"` 26 + Amount uint64 `json:"amount" minimum:"30"` 27 + Type string `json:"type"` 28 + } 29 + 30 + type updateTransactionSchema struct { 31 + *CreateTransactionSchema 32 + ID uint64 33 + } 34 + 35 + func getTransactions(ctx context.Context, db *bun.DB, input *walletIDInput) (*[]TransactionModel, error) { 36 + transactions := &[]TransactionModel{} 37 + err := db.NewSelect().Model(transactions).Relation("Wallet").Where("wallet_id = ?", input.WalletID).Scan(ctx) 38 + if err != nil { 39 + return nil, err 40 + } 41 + return transactions, nil 42 + } 43 + 44 + func getTransactionByID(ctx context.Context, db *bun.DB, input *transactionIDInput) (*TransactionModel, error) { 45 + transaction := &TransactionModel{} 46 + err := db.NewSelect().Model(transaction).Where("id = ?", input.ID).Relation("Wallet").Scan(ctx) 47 + if err != nil { 48 + return nil, err 49 + } 50 + return transaction, nil 51 + } 52 + 53 + func createTransaction(ctx context.Context, db *bun.DB, schema *CreateTransactionSchema) (*TransactionModel, error) { 54 + transaction := &TransactionModel{Reason: schema.Reason, Amount: schema.Amount, Type: schema.Type, WalletID: schema.WalletID} 55 + _, err := db.NewInsert().Model(transaction).Exec(ctx) 56 + if err != nil { 57 + return nil, err 58 + } 59 + return transaction, nil 60 + } 61 + 62 + func updateTransaction(ctx context.Context, db *bun.DB, schema *updateTransactionSchema) (*TransactionModel, error) { 63 + transaction := &TransactionModel{Reason: schema.Reason, Amount: schema.Amount, Type: schema.Type} 64 + _, err := db.NewUpdate().Model(transaction).Where("id = ?", schema.ID).Exec(ctx) 65 + if err != nil { 66 + return nil, err 67 + } 68 + return transaction, nil 69 + } 70 + 71 + func deleteTransaction(ctx context.Context, db *bun.DB, transactionID uint64) error { 72 + _, err := db.NewDelete().Model((*TransactionModel)(nil)).Where("id = ?", transactionID).Exec(ctx) 73 + return err 74 + }
+106
backend/transaction/transaction_router.go
··· 1 + package transaction 2 + 3 + import ( 4 + "context" 5 + 6 + "github.com/danielgtaylor/huma/v2" 7 + "github.com/uptrace/bun" 8 + ) 9 + 10 + type handler struct { 11 + db *bun.DB 12 + } 13 + 14 + type walletIDInput struct { 15 + WalletID uint64 `path:"walletID"` 16 + } 17 + 18 + type transactionIDInput struct { 19 + WalletID uint64 `path:"walletID"` 20 + ID uint64 `path:"transactionID"` 21 + } 22 + 23 + func New(db *bun.DB) *handler { 24 + return &handler{ 25 + db: db, 26 + } 27 + } 28 + 29 + func (self *handler) Register(api huma.API) { 30 + huma.Get(api, "/wallet/{walletID}/transactions", self.list) 31 + huma.Post(api, "/wallet/{walletID}/transactions", self.create) 32 + huma.Get(api, "/wallet/{walletID}/transaction/{transactionID}", self.show) 33 + huma.Put(api, "/wallet/{walletID}/transaction/{transactionID}", self.update) 34 + huma.Delete(api, "/wallet/{walletID}/transaction/{transactionID}", self.delete) 35 + } 36 + 37 + type transactionsResp struct { 38 + Body *[]TransactionModel 39 + } 40 + 41 + func (self *handler) list(ctx context.Context, input *walletIDInput) (*transactionsResp, error) { 42 + transactions, err := getTransactions(ctx, self.db, input) 43 + if err != nil { 44 + return nil, huma.Error400BadRequest(err.Error(), err) 45 + } 46 + resp := &transactionsResp{} 47 + resp.Body = transactions 48 + return resp, nil 49 + } 50 + 51 + type transactionResp struct { 52 + Body *TransactionModel 53 + } 54 + 55 + func (self *handler) show(ctx context.Context, input *transactionIDInput) (*transactionResp, error) { 56 + transaction, err := getTransactionByID(ctx, self.db, input) 57 + if err != nil { 58 + return nil, huma.Error404NotFound(err.Error(), err) 59 + } 60 + resp := &transactionResp{} 61 + resp.Body = transaction 62 + return resp, nil 63 + } 64 + 65 + type createTransactionInput struct { 66 + WalletID uint64 `path:"walletID"` 67 + Body CreateTransactionSchema 68 + } 69 + 70 + func (self *handler) create(ctx context.Context, input *createTransactionInput) (*transactionResp, error) { 71 + transaction, err := createTransaction(ctx, self.db, &input.Body) 72 + if err != nil { 73 + return nil, huma.Error400BadRequest(err.Error(), err) 74 + } 75 + resp := &transactionResp{} 76 + resp.Body = transaction 77 + return resp, nil 78 + } 79 + 80 + type updateTransactionInput struct { 81 + Body updateTransactionSchema 82 + } 83 + 84 + func (self *handler) update(ctx context.Context, input *updateTransactionInput) (*transactionResp, error) { 85 + transaction, err := updateTransaction(ctx, self.db, &input.Body) 86 + if err != nil { 87 + return nil, huma.Error400BadRequest(err.Error(), err) 88 + } 89 + resp := &transactionResp{} 90 + resp.Body = transaction 91 + return resp, nil 92 + } 93 + 94 + type deleteTransactionResp struct { 95 + Body string 96 + } 97 + 98 + func (self *handler) delete(ctx context.Context, input *transactionIDInput) (*deleteTransactionResp, error) { 99 + err := deleteTransaction(ctx, self.db, input.ID) 100 + if err != nil { 101 + return nil, huma.Error400BadRequest(err.Error(), err) 102 + } 103 + resp := &deleteTransactionResp{} 104 + resp.Body = "ok" 105 + return resp, nil 106 + }
+1 -1
backend/wallet/wallet_model.go
··· 16 16 } 17 17 18 18 type CreateWalletSchema struct { 19 - Name string `json:"name"` 19 + Name string `json:"name" minLength:"1"` 20 20 Balance uint64 `json:"balance"` 21 21 } 22 22
+604 -351
frontend/src/api/schema.d.ts
··· 1 1 export interface paths { 2 - '/me': { 3 - parameters: { 4 - query?: never; 5 - header?: never; 6 - path?: never; 7 - cookie?: never; 8 - }; 9 - /** Get me */ 10 - get: operations['get-me']; 11 - put?: never; 12 - post?: never; 13 - delete?: never; 14 - options?: never; 15 - head?: never; 16 - patch?: never; 17 - trace?: never; 18 - }; 19 - '/wallet/{walletId}': { 20 - parameters: { 21 - query?: never; 22 - header?: never; 23 - path?: never; 24 - cookie?: never; 25 - }; 26 - /** Get wallet by wallet ID */ 27 - get: operations['get-wallet-by-wallet-id']; 28 - /** Put wallet by wallet ID */ 29 - put: operations['put-wallet-by-wallet-id']; 30 - post?: never; 31 - /** Delete wallet by wallet ID */ 32 - delete: operations['delete-wallet-by-wallet-id']; 33 - options?: never; 34 - head?: never; 35 - patch?: never; 36 - trace?: never; 37 - }; 38 - '/wallets': { 39 - parameters: { 40 - query?: never; 41 - header?: never; 42 - path?: never; 43 - cookie?: never; 44 - }; 45 - /** List wallets */ 46 - get: operations['list-wallets']; 47 - put?: never; 48 - /** Post wallets */ 49 - post: operations['post-wallets']; 50 - delete?: never; 51 - options?: never; 52 - head?: never; 53 - patch?: never; 54 - trace?: never; 55 - }; 2 + "/me": { 3 + parameters: { 4 + query?: never; 5 + header?: never; 6 + path?: never; 7 + cookie?: never; 8 + }; 9 + /** Get me */ 10 + get: operations["get-me"]; 11 + put?: never; 12 + post?: never; 13 + delete?: never; 14 + options?: never; 15 + head?: never; 16 + patch?: never; 17 + trace?: never; 18 + }; 19 + "/wallet/{walletID}/transaction/{transactionID}": { 20 + parameters: { 21 + query?: never; 22 + header?: never; 23 + path?: never; 24 + cookie?: never; 25 + }; 26 + /** Get wallet by wallet ID transaction by transaction ID */ 27 + get: operations["get-wallet-by-wallet-id-transaction-by-transaction-id"]; 28 + /** Put wallet by wallet ID transaction by transaction ID */ 29 + put: operations["put-wallet-by-wallet-id-transaction-by-transaction-id"]; 30 + post?: never; 31 + /** Delete wallet by wallet ID transaction by transaction ID */ 32 + delete: operations["delete-wallet-by-wallet-id-transaction-by-transaction-id"]; 33 + options?: never; 34 + head?: never; 35 + patch?: never; 36 + trace?: never; 37 + }; 38 + "/wallet/{walletID}/transactions": { 39 + parameters: { 40 + query?: never; 41 + header?: never; 42 + path?: never; 43 + cookie?: never; 44 + }; 45 + /** List wallet by wallet ID transactions */ 46 + get: operations["list-wallet-by-wallet-id-transactions"]; 47 + put?: never; 48 + /** Post wallet by wallet ID transactions */ 49 + post: operations["post-wallet-by-wallet-id-transactions"]; 50 + delete?: never; 51 + options?: never; 52 + head?: never; 53 + patch?: never; 54 + trace?: never; 55 + }; 56 + "/wallet/{walletId}": { 57 + parameters: { 58 + query?: never; 59 + header?: never; 60 + path?: never; 61 + cookie?: never; 62 + }; 63 + /** Get wallet by wallet ID */ 64 + get: operations["get-wallet-by-wallet-id"]; 65 + /** Put wallet by wallet ID */ 66 + put: operations["put-wallet-by-wallet-id"]; 67 + post?: never; 68 + /** Delete wallet by wallet ID */ 69 + delete: operations["delete-wallet-by-wallet-id"]; 70 + options?: never; 71 + head?: never; 72 + patch?: never; 73 + trace?: never; 74 + }; 75 + "/wallets": { 76 + parameters: { 77 + query?: never; 78 + header?: never; 79 + path?: never; 80 + cookie?: never; 81 + }; 82 + /** List wallets */ 83 + get: operations["list-wallets"]; 84 + put?: never; 85 + /** Post wallets */ 86 + post: operations["post-wallets"]; 87 + delete?: never; 88 + options?: never; 89 + head?: never; 90 + patch?: never; 91 + trace?: never; 92 + }; 56 93 } 57 94 export type webhooks = Record<string, never>; 58 95 export interface components { 59 - schemas: { 60 - CreateWalletSchema: { 61 - /** 62 - * Format: uri 63 - * @description A URL to the JSON Schema for this object. 64 - * @example https://example.com/schemas/CreateWalletSchema.json 65 - */ 66 - readonly $schema?: string; 67 - /** Format: int64 */ 68 - balance: number; 69 - name: string; 70 - }; 71 - ErrorDetail: { 72 - /** @description Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' */ 73 - location?: string; 74 - /** @description Error message text */ 75 - message?: string; 76 - /** @description The value at the given location */ 77 - value?: unknown; 78 - }; 79 - ErrorModel: { 80 - /** 81 - * Format: uri 82 - * @description A URL to the JSON Schema for this object. 83 - * @example https://example.com/schemas/ErrorModel.json 84 - */ 85 - readonly $schema?: string; 86 - /** 87 - * @description A human-readable explanation specific to this occurrence of the problem. 88 - * @example Property foo is required but is missing. 89 - */ 90 - detail?: string; 91 - /** @description Optional list of individual error details */ 92 - errors?: components['schemas']['ErrorDetail'][] | null; 93 - /** 94 - * Format: uri 95 - * @description A URI reference that identifies the specific occurrence of the problem. 96 - * @example https://example.com/error-log/abc123 97 - */ 98 - instance?: string; 99 - /** 100 - * Format: int64 101 - * @description HTTP status code 102 - * @example 400 103 - */ 104 - status?: number; 105 - /** 106 - * @description A short, human-readable summary of the problem type. This value should not change between occurrences of the error. 107 - * @example Bad Request 108 - */ 109 - title?: string; 110 - /** 111 - * Format: uri 112 - * @description A URI reference to human-readable documentation for the error. 113 - * @default about:blank 114 - * @example https://example.com/errors/example 115 - */ 116 - type: string; 117 - }; 118 - UpdateWalletSchema: { 119 - /** 120 - * Format: uri 121 - * @description A URL to the JSON Schema for this object. 122 - * @example https://example.com/schemas/UpdateWalletSchema.json 123 - */ 124 - readonly $schema?: string; 125 - /** Format: int64 */ 126 - ID: number; 127 - /** Format: int64 */ 128 - balance: number; 129 - name: string; 130 - }; 131 - UserModel: { 132 - /** 133 - * Format: uri 134 - * @description A URL to the JSON Schema for this object. 135 - * @example https://example.com/schemas/UserModel.json 136 - */ 137 - readonly $schema?: string; 138 - AccountDID: string; 139 - /** Format: date-time */ 140 - CreatedAt: string; 141 - Handle: string; 142 - /** Format: date-time */ 143 - UpdatedAt: string; 144 - }; 145 - WalletModel: { 146 - /** 147 - * Format: uri 148 - * @description A URL to the JSON Schema for this object. 149 - * @example https://example.com/schemas/WalletModel.json 150 - */ 151 - readonly $schema?: string; 152 - /** Format: int64 */ 153 - Balance: number; 154 - /** Format: date-time */ 155 - CreatedAt: string; 156 - /** Format: date-time */ 157 - DeletedAt: string; 158 - /** Format: int64 */ 159 - ID: number; 160 - Name: string; 161 - /** Format: date-time */ 162 - UpdatedAt: string; 163 - }; 164 - }; 165 - responses: never; 166 - parameters: never; 167 - requestBodies: never; 168 - headers: never; 169 - pathItems: never; 96 + schemas: { 97 + CreateTransactionSchema: { 98 + /** 99 + * Format: uri 100 + * @description A URL to the JSON Schema for this object. 101 + * @example https://example.com/schemas/CreateTransactionSchema.json 102 + */ 103 + readonly $schema?: string; 104 + /** Format: int64 */ 105 + amount: number; 106 + reason: string; 107 + type: string; 108 + /** Format: int64 */ 109 + walletID: number; 110 + }; 111 + CreateWalletSchema: { 112 + /** 113 + * Format: uri 114 + * @description A URL to the JSON Schema for this object. 115 + * @example https://example.com/schemas/CreateWalletSchema.json 116 + */ 117 + readonly $schema?: string; 118 + /** Format: int64 */ 119 + balance: number; 120 + name: string; 121 + }; 122 + ErrorDetail: { 123 + /** @description Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id' */ 124 + location?: string; 125 + /** @description Error message text */ 126 + message?: string; 127 + /** @description The value at the given location */ 128 + value?: unknown; 129 + }; 130 + ErrorModel: { 131 + /** 132 + * Format: uri 133 + * @description A URL to the JSON Schema for this object. 134 + * @example https://example.com/schemas/ErrorModel.json 135 + */ 136 + readonly $schema?: string; 137 + /** 138 + * @description A human-readable explanation specific to this occurrence of the problem. 139 + * @example Property foo is required but is missing. 140 + */ 141 + detail?: string; 142 + /** @description Optional list of individual error details */ 143 + errors?: components["schemas"]["ErrorDetail"][] | null; 144 + /** 145 + * Format: uri 146 + * @description A URI reference that identifies the specific occurrence of the problem. 147 + * @example https://example.com/error-log/abc123 148 + */ 149 + instance?: string; 150 + /** 151 + * Format: int64 152 + * @description HTTP status code 153 + * @example 400 154 + */ 155 + status?: number; 156 + /** 157 + * @description A short, human-readable summary of the problem type. This value should not change between occurrences of the error. 158 + * @example Bad Request 159 + */ 160 + title?: string; 161 + /** 162 + * Format: uri 163 + * @description A URI reference to human-readable documentation for the error. 164 + * @default about:blank 165 + * @example https://example.com/errors/example 166 + */ 167 + type: string; 168 + }; 169 + TransactionModel: { 170 + /** 171 + * Format: uri 172 + * @description A URL to the JSON Schema for this object. 173 + * @example https://example.com/schemas/TransactionModel.json 174 + */ 175 + readonly $schema?: string; 176 + /** Format: int64 */ 177 + Amount: number; 178 + /** Format: date-time */ 179 + CreatedAt: string; 180 + /** Format: date-time */ 181 + DeletedAt: string; 182 + /** Format: int64 */ 183 + ID: number; 184 + Reason: string; 185 + Type: string; 186 + /** Format: date-time */ 187 + UpdatedAt: string; 188 + Wallet: components["schemas"]["WalletModel"]; 189 + /** Format: int64 */ 190 + WalletID: number; 191 + }; 192 + UpdateTransactionSchema: { 193 + /** 194 + * Format: uri 195 + * @description A URL to the JSON Schema for this object. 196 + * @example https://example.com/schemas/UpdateTransactionSchema.json 197 + */ 198 + readonly $schema?: string; 199 + /** Format: int64 */ 200 + ID: number; 201 + /** Format: int64 */ 202 + amount: number; 203 + reason: string; 204 + type: string; 205 + /** Format: int64 */ 206 + walletID: number; 207 + }; 208 + UpdateWalletSchema: { 209 + /** 210 + * Format: uri 211 + * @description A URL to the JSON Schema for this object. 212 + * @example https://example.com/schemas/UpdateWalletSchema.json 213 + */ 214 + readonly $schema?: string; 215 + /** Format: int64 */ 216 + ID: number; 217 + /** Format: int64 */ 218 + balance: number; 219 + name: string; 220 + }; 221 + UserModel: { 222 + /** 223 + * Format: uri 224 + * @description A URL to the JSON Schema for this object. 225 + * @example https://example.com/schemas/UserModel.json 226 + */ 227 + readonly $schema?: string; 228 + AccountDID: string; 229 + /** Format: date-time */ 230 + CreatedAt: string; 231 + Handle: string; 232 + /** Format: date-time */ 233 + UpdatedAt: string; 234 + }; 235 + WalletModel: { 236 + /** 237 + * Format: uri 238 + * @description A URL to the JSON Schema for this object. 239 + * @example https://example.com/schemas/WalletModel.json 240 + */ 241 + readonly $schema?: string; 242 + /** Format: int64 */ 243 + Balance: number; 244 + /** Format: date-time */ 245 + CreatedAt: string; 246 + /** Format: date-time */ 247 + DeletedAt: string; 248 + /** Format: int64 */ 249 + ID: number; 250 + Name: string; 251 + /** Format: date-time */ 252 + UpdatedAt: string; 253 + }; 254 + }; 255 + responses: never; 256 + parameters: never; 257 + requestBodies: never; 258 + headers: never; 259 + pathItems: never; 170 260 } 171 261 export type $defs = Record<string, never>; 172 262 export interface operations { 173 - 'get-me': { 174 - parameters: { 175 - query?: never; 176 - header?: never; 177 - path?: never; 178 - cookie?: never; 179 - }; 180 - requestBody?: never; 181 - responses: { 182 - /** @description OK */ 183 - 200: { 184 - headers: { 185 - [name: string]: unknown; 186 - }; 187 - content: { 188 - 'application/json': components['schemas']['UserModel']; 189 - }; 190 - }; 191 - /** @description Error */ 192 - default: { 193 - headers: { 194 - [name: string]: unknown; 195 - }; 196 - content: { 197 - 'application/problem+json': components['schemas']['ErrorModel']; 198 - }; 199 - }; 200 - }; 201 - }; 202 - 'get-wallet-by-wallet-id': { 203 - parameters: { 204 - query?: never; 205 - header?: never; 206 - path: { 207 - walletId: number; 208 - }; 209 - cookie?: never; 210 - }; 211 - requestBody?: never; 212 - responses: { 213 - /** @description OK */ 214 - 200: { 215 - headers: { 216 - [name: string]: unknown; 217 - }; 218 - content: { 219 - 'application/json': components['schemas']['WalletModel']; 220 - }; 221 - }; 222 - /** @description Error */ 223 - default: { 224 - headers: { 225 - [name: string]: unknown; 226 - }; 227 - content: { 228 - 'application/problem+json': components['schemas']['ErrorModel']; 229 - }; 230 - }; 231 - }; 232 - }; 233 - 'put-wallet-by-wallet-id': { 234 - parameters: { 235 - query?: never; 236 - header?: never; 237 - path?: never; 238 - cookie?: never; 239 - }; 240 - requestBody: { 241 - content: { 242 - 'application/json': components['schemas']['UpdateWalletSchema']; 243 - }; 244 - }; 245 - responses: { 246 - /** @description OK */ 247 - 200: { 248 - headers: { 249 - [name: string]: unknown; 250 - }; 251 - content: { 252 - 'application/json': components['schemas']['WalletModel']; 253 - }; 254 - }; 255 - /** @description Error */ 256 - default: { 257 - headers: { 258 - [name: string]: unknown; 259 - }; 260 - content: { 261 - 'application/problem+json': components['schemas']['ErrorModel']; 262 - }; 263 - }; 264 - }; 265 - }; 266 - 'delete-wallet-by-wallet-id': { 267 - parameters: { 268 - query?: never; 269 - header?: never; 270 - path: { 271 - walletId: number; 272 - }; 273 - cookie?: never; 274 - }; 275 - requestBody?: never; 276 - responses: { 277 - /** @description OK */ 278 - 200: { 279 - headers: { 280 - [name: string]: unknown; 281 - }; 282 - content: { 283 - 'application/json': string; 284 - }; 285 - }; 286 - /** @description Error */ 287 - default: { 288 - headers: { 289 - [name: string]: unknown; 290 - }; 291 - content: { 292 - 'application/problem+json': components['schemas']['ErrorModel']; 293 - }; 294 - }; 295 - }; 296 - }; 297 - 'list-wallets': { 298 - parameters: { 299 - query?: never; 300 - header?: never; 301 - path?: never; 302 - cookie?: never; 303 - }; 304 - requestBody?: never; 305 - responses: { 306 - /** @description OK */ 307 - 200: { 308 - headers: { 309 - [name: string]: unknown; 310 - }; 311 - content: { 312 - 'application/json': components['schemas']['WalletModel'][] | null; 313 - }; 314 - }; 315 - /** @description Error */ 316 - default: { 317 - headers: { 318 - [name: string]: unknown; 319 - }; 320 - content: { 321 - 'application/problem+json': components['schemas']['ErrorModel']; 322 - }; 323 - }; 324 - }; 325 - }; 326 - 'post-wallets': { 327 - parameters: { 328 - query?: never; 329 - header?: never; 330 - path?: never; 331 - cookie?: never; 332 - }; 333 - requestBody: { 334 - content: { 335 - 'application/json': components['schemas']['CreateWalletSchema']; 336 - }; 337 - }; 338 - responses: { 339 - /** @description OK */ 340 - 200: { 341 - headers: { 342 - [name: string]: unknown; 343 - }; 344 - content: { 345 - 'application/json': components['schemas']['WalletModel']; 346 - }; 347 - }; 348 - /** @description Error */ 349 - default: { 350 - headers: { 351 - [name: string]: unknown; 352 - }; 353 - content: { 354 - 'application/problem+json': components['schemas']['ErrorModel']; 355 - }; 356 - }; 357 - }; 358 - }; 263 + "get-me": { 264 + parameters: { 265 + query?: never; 266 + header?: never; 267 + path?: never; 268 + cookie?: never; 269 + }; 270 + requestBody?: never; 271 + responses: { 272 + /** @description OK */ 273 + 200: { 274 + headers: { 275 + [name: string]: unknown; 276 + }; 277 + content: { 278 + "application/json": components["schemas"]["UserModel"]; 279 + }; 280 + }; 281 + /** @description Error */ 282 + default: { 283 + headers: { 284 + [name: string]: unknown; 285 + }; 286 + content: { 287 + "application/problem+json": components["schemas"]["ErrorModel"]; 288 + }; 289 + }; 290 + }; 291 + }; 292 + "get-wallet-by-wallet-id-transaction-by-transaction-id": { 293 + parameters: { 294 + query?: never; 295 + header?: never; 296 + path: { 297 + walletID: number; 298 + transactionID: number; 299 + }; 300 + cookie?: never; 301 + }; 302 + requestBody?: never; 303 + responses: { 304 + /** @description OK */ 305 + 200: { 306 + headers: { 307 + [name: string]: unknown; 308 + }; 309 + content: { 310 + "application/json": components["schemas"]["TransactionModel"]; 311 + }; 312 + }; 313 + /** @description Error */ 314 + default: { 315 + headers: { 316 + [name: string]: unknown; 317 + }; 318 + content: { 319 + "application/problem+json": components["schemas"]["ErrorModel"]; 320 + }; 321 + }; 322 + }; 323 + }; 324 + "put-wallet-by-wallet-id-transaction-by-transaction-id": { 325 + parameters: { 326 + query?: never; 327 + header?: never; 328 + path?: never; 329 + cookie?: never; 330 + }; 331 + requestBody: { 332 + content: { 333 + "application/json": components["schemas"]["UpdateTransactionSchema"]; 334 + }; 335 + }; 336 + responses: { 337 + /** @description OK */ 338 + 200: { 339 + headers: { 340 + [name: string]: unknown; 341 + }; 342 + content: { 343 + "application/json": components["schemas"]["TransactionModel"]; 344 + }; 345 + }; 346 + /** @description Error */ 347 + default: { 348 + headers: { 349 + [name: string]: unknown; 350 + }; 351 + content: { 352 + "application/problem+json": components["schemas"]["ErrorModel"]; 353 + }; 354 + }; 355 + }; 356 + }; 357 + "delete-wallet-by-wallet-id-transaction-by-transaction-id": { 358 + parameters: { 359 + query?: never; 360 + header?: never; 361 + path: { 362 + walletID: number; 363 + transactionID: number; 364 + }; 365 + cookie?: never; 366 + }; 367 + requestBody?: never; 368 + responses: { 369 + /** @description OK */ 370 + 200: { 371 + headers: { 372 + [name: string]: unknown; 373 + }; 374 + content: { 375 + "application/json": string; 376 + }; 377 + }; 378 + /** @description Error */ 379 + default: { 380 + headers: { 381 + [name: string]: unknown; 382 + }; 383 + content: { 384 + "application/problem+json": components["schemas"]["ErrorModel"]; 385 + }; 386 + }; 387 + }; 388 + }; 389 + "list-wallet-by-wallet-id-transactions": { 390 + parameters: { 391 + query?: never; 392 + header?: never; 393 + path: { 394 + walletID: number; 395 + }; 396 + cookie?: never; 397 + }; 398 + requestBody?: never; 399 + responses: { 400 + /** @description OK */ 401 + 200: { 402 + headers: { 403 + [name: string]: unknown; 404 + }; 405 + content: { 406 + "application/json": components["schemas"]["TransactionModel"][] | null; 407 + }; 408 + }; 409 + /** @description Error */ 410 + default: { 411 + headers: { 412 + [name: string]: unknown; 413 + }; 414 + content: { 415 + "application/problem+json": components["schemas"]["ErrorModel"]; 416 + }; 417 + }; 418 + }; 419 + }; 420 + "post-wallet-by-wallet-id-transactions": { 421 + parameters: { 422 + query?: never; 423 + header?: never; 424 + path: { 425 + walletID: number; 426 + }; 427 + cookie?: never; 428 + }; 429 + requestBody: { 430 + content: { 431 + "application/json": components["schemas"]["CreateTransactionSchema"]; 432 + }; 433 + }; 434 + responses: { 435 + /** @description OK */ 436 + 200: { 437 + headers: { 438 + [name: string]: unknown; 439 + }; 440 + content: { 441 + "application/json": components["schemas"]["TransactionModel"]; 442 + }; 443 + }; 444 + /** @description Error */ 445 + default: { 446 + headers: { 447 + [name: string]: unknown; 448 + }; 449 + content: { 450 + "application/problem+json": components["schemas"]["ErrorModel"]; 451 + }; 452 + }; 453 + }; 454 + }; 455 + "get-wallet-by-wallet-id": { 456 + parameters: { 457 + query?: never; 458 + header?: never; 459 + path: { 460 + walletId: number; 461 + }; 462 + cookie?: never; 463 + }; 464 + requestBody?: never; 465 + responses: { 466 + /** @description OK */ 467 + 200: { 468 + headers: { 469 + [name: string]: unknown; 470 + }; 471 + content: { 472 + "application/json": components["schemas"]["WalletModel"]; 473 + }; 474 + }; 475 + /** @description Error */ 476 + default: { 477 + headers: { 478 + [name: string]: unknown; 479 + }; 480 + content: { 481 + "application/problem+json": components["schemas"]["ErrorModel"]; 482 + }; 483 + }; 484 + }; 485 + }; 486 + "put-wallet-by-wallet-id": { 487 + parameters: { 488 + query?: never; 489 + header?: never; 490 + path?: never; 491 + cookie?: never; 492 + }; 493 + requestBody: { 494 + content: { 495 + "application/json": components["schemas"]["UpdateWalletSchema"]; 496 + }; 497 + }; 498 + responses: { 499 + /** @description OK */ 500 + 200: { 501 + headers: { 502 + [name: string]: unknown; 503 + }; 504 + content: { 505 + "application/json": components["schemas"]["WalletModel"]; 506 + }; 507 + }; 508 + /** @description Error */ 509 + default: { 510 + headers: { 511 + [name: string]: unknown; 512 + }; 513 + content: { 514 + "application/problem+json": components["schemas"]["ErrorModel"]; 515 + }; 516 + }; 517 + }; 518 + }; 519 + "delete-wallet-by-wallet-id": { 520 + parameters: { 521 + query?: never; 522 + header?: never; 523 + path: { 524 + walletId: number; 525 + }; 526 + cookie?: never; 527 + }; 528 + requestBody?: never; 529 + responses: { 530 + /** @description OK */ 531 + 200: { 532 + headers: { 533 + [name: string]: unknown; 534 + }; 535 + content: { 536 + "application/json": string; 537 + }; 538 + }; 539 + /** @description Error */ 540 + default: { 541 + headers: { 542 + [name: string]: unknown; 543 + }; 544 + content: { 545 + "application/problem+json": components["schemas"]["ErrorModel"]; 546 + }; 547 + }; 548 + }; 549 + }; 550 + "list-wallets": { 551 + parameters: { 552 + query?: never; 553 + header?: never; 554 + path?: never; 555 + cookie?: never; 556 + }; 557 + requestBody?: never; 558 + responses: { 559 + /** @description OK */ 560 + 200: { 561 + headers: { 562 + [name: string]: unknown; 563 + }; 564 + content: { 565 + "application/json": components["schemas"]["WalletModel"][] | null; 566 + }; 567 + }; 568 + /** @description Error */ 569 + default: { 570 + headers: { 571 + [name: string]: unknown; 572 + }; 573 + content: { 574 + "application/problem+json": components["schemas"]["ErrorModel"]; 575 + }; 576 + }; 577 + }; 578 + }; 579 + "post-wallets": { 580 + parameters: { 581 + query?: never; 582 + header?: never; 583 + path?: never; 584 + cookie?: never; 585 + }; 586 + requestBody: { 587 + content: { 588 + "application/json": components["schemas"]["CreateWalletSchema"]; 589 + }; 590 + }; 591 + responses: { 592 + /** @description OK */ 593 + 200: { 594 + headers: { 595 + [name: string]: unknown; 596 + }; 597 + content: { 598 + "application/json": components["schemas"]["WalletModel"]; 599 + }; 600 + }; 601 + /** @description Error */ 602 + default: { 603 + headers: { 604 + [name: string]: unknown; 605 + }; 606 + content: { 607 + "application/problem+json": components["schemas"]["ErrorModel"]; 608 + }; 609 + }; 610 + }; 611 + }; 359 612 }
+58 -2
frontend/src/api/wallet.ts
··· 1 1 import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'; 2 - import { proxyRefs } from 'vue'; 2 + import { proxyRefs, toValue, type MaybeRef } from 'vue'; 3 + 4 + import type { components } from './schema'; 3 5 4 6 import { openAPIClient } from './client'; 5 7 ··· 18 20 return proxyRefs(useQuery(walletsQueryOpts())); 19 21 } 20 22 23 + export function walletQueryOpts(walletID: MaybeRef<number>) { 24 + return queryOptions({ 25 + queryKey: ['wallets', walletID], 26 + queryFn: async () => { 27 + const { data, error } = await openAPIClient.GET('/wallet/{walletId}', { 28 + params: { path: { walletId: toValue(walletID) } }, 29 + }); 30 + if (error) throw error; 31 + return data; 32 + }, 33 + }); 34 + } 35 + 36 + export function useWallet(walletID: MaybeRef<number>) { 37 + return proxyRefs(useQuery(walletQueryOpts(walletID))); 38 + } 39 + 21 40 export function useNewWallet() { 22 41 const qc = useQueryClient(); 23 42 return proxyRefs( 24 43 useMutation({ 25 - mutationFn: async (body: Parameters<typeof openAPIClient.POST>['1']['body']) => { 44 + mutationFn: async (body: components['schemas']['CreateWalletSchema']) => { 26 45 const { data, error } = await openAPIClient.POST('/wallets', { body }); 27 46 if (error) throw error; 28 47 return data; ··· 33 52 }), 34 53 ); 35 54 } 55 + 56 + export function txsQueryOpts(walletID: MaybeRef<number>) { 57 + return queryOptions({ 58 + queryKey: [walletID, 'txs'], 59 + queryFn: async () => { 60 + const { data, error } = await openAPIClient.GET('/wallet/{walletID}/transactions', { 61 + params: { path: { walletID: toValue(walletID) } }, 62 + }); 63 + if (error) throw error; 64 + return data; 65 + }, 66 + }); 67 + } 68 + 69 + export function useTransactions(walletID: MaybeRef<number>) { 70 + return proxyRefs(useQuery(txsQueryOpts(walletID))); 71 + } 72 + 73 + export function useNewTransaction() { 74 + const qc = useQueryClient(); 75 + return proxyRefs( 76 + useMutation({ 77 + mutationFn: async (body: components['schemas']['CreateTransactionSchema']) => { 78 + const { data, error } = await openAPIClient.POST('/wallet/{walletID}/transactions', { 79 + body, 80 + params: { path: { walletID: body.walletID } }, 81 + }); 82 + if (error) throw error; 83 + return data; 84 + }, 85 + onSuccess: async (_, { walletID }) => { 86 + await qc.invalidateQueries({ queryKey: walletQueryOpts(walletID).queryKey }); 87 + await qc.invalidateQueries({ queryKey: txsQueryOpts(walletID).queryKey }); 88 + }, 89 + }), 90 + ); 91 + }
+32 -2
frontend/src/routes/+root-layout.vue
··· 1 - <script setup lang="ts"></script> 1 + <script setup lang="ts"> 2 + import type { NavigationMenuItem } from '@nuxt/ui'; 3 + import { RouteTyped } from './+route.gen'; 4 + import { useMe } from '../api/user'; 5 + 6 + const router = RouteTyped.useRouter(); 7 + const me = useMe(); 8 + 9 + const links: NavigationMenuItem[] = [ 10 + { 11 + label: 'Home', 12 + to: router.href('/'), 13 + }, 14 + { 15 + label: 'Wallets', 16 + to: router.href('/wallets'), 17 + }, 18 + ]; 19 + </script> 2 20 3 21 <template> 4 22 <UMain> 5 - <slot></slot> 23 + <UDashboardGroup> 24 + <UDashboardSidebar resizable> 25 + <UNavigationMenu :items="links" orientation="vertical" /> 26 + </UDashboardSidebar> 27 + <UDashboardPanel resizable> 28 + <template #header> 29 + <UDashboardNavbar> Hello @{{ me.data?.Handle }} </UDashboardNavbar> 30 + </template> 31 + <template #body> 32 + <slot></slot> 33 + </template> 34 + </UDashboardPanel> 35 + </UDashboardGroup> 6 36 </UMain> 7 37 </template>
-1
frontend/src/routes/+root-page.vue
··· 6 6 7 7 <template> 8 8 <pre>{{ me.data }}</pre> 9 - <a href="/wallets">Wallets</a> 10 9 </template>
+32 -32
frontend/src/routes/wallets/+wallets-page.vue
··· 1 1 <script setup lang="ts"> 2 - import { ref, shallowReactive } from 'vue'; 2 + import { ref, shallowReactive, shallowRef } from 'vue'; 3 3 import { useNewWallet, useWallets } from '../../api/wallet'; 4 4 import { RouteTyped } from './+route.gen'; 5 5 ··· 7 7 name: '', 8 8 balance: 0, 9 9 }); 10 + const dialogOpened = shallowRef(false); 10 11 11 12 const router = RouteTyped.useRouter(); 12 13 const wallets = useWallets(); 13 14 const newWallet = useNewWallet(); 14 15 15 - const isCreating = ref(false); 16 - 17 16 function handleNewWallet() { 18 17 newWallet.mutate(walletForm, { 19 18 onSuccess: () => { 20 - isCreating.value = false; 19 + dialogOpened.value = false; 21 20 walletForm.balance = 0; 22 21 walletForm.name = ''; 23 22 }, ··· 26 25 </script> 27 26 28 27 <template> 29 - <div class="flex justify-end items-center"> 30 - <UButton @click="isCreating = true">Create a New Wallet</UButton> 31 - </div> 28 + <UModal title="Create a Wallet" v-model:open="dialogOpened"> 29 + <UButton class="ml-auto">Create a Wallet</UButton> 32 30 33 - <UForm 34 - v-show="isCreating" 35 - class="w-75 sm:w-100 flex flex-col gap-4" 36 - :state="walletForm" 37 - @submit="handleNewWallet" 38 - > 39 - <UFormField label="Wallet Name" name="name"> 40 - <UInput 41 - type="text" 42 - placeholder="USD Wallet or TWD Wallet" 43 - class="w-full" 44 - v-model="walletForm.name" 45 - /> 46 - </UFormField> 31 + <template #body> 32 + <UForm class="flex flex-col gap-4" :state="walletForm" @submit="handleNewWallet"> 33 + <UFormField label="Wallet Name" name="name"> 34 + <UInput 35 + type="text" 36 + placeholder="USD Wallet or TWD Wallet" 37 + class="w-full" 38 + v-model="walletForm.name" 39 + /> 40 + </UFormField> 47 41 48 - <UFormField label="Current Balance" name="balance"> 49 - <UInput type="number" class="w-full" v-model="walletForm.balance" /> 50 - </UFormField> 42 + <UFormField label="Current Balance" name="balance"> 43 + <UInput type="number" class="w-full" v-model="walletForm.balance" /> 44 + </UFormField> 51 45 52 - <UButton :loading="newWallet.isPending" type="submit">Create</UButton> 46 + <UButton class="self-end" :loading="newWallet.isPending" type="submit">Create</UButton> 53 47 54 - <UAlert v-if="newWallet.isError"> 55 - {{ newWallet.error }} 56 - </UAlert> 57 - </UForm> 48 + <UAlert v-if="newWallet.isError" color="error" variant="subtle"> 49 + <template #title> 50 + {{ newWallet.error }} 51 + </template> 52 + </UAlert> 53 + </UForm> 54 + </template> 55 + </UModal> 58 56 59 57 <template v-for="wallet in wallets.data" :key="wallet.ID"> 60 - <a :href="router.href({ path: '/wallets/:walletId', params: { walletId: wallet.ID } })"> 58 + <a :href="router.href({ path: '/wallets/:walletID', params: { walletID: wallet.ID } })"> 61 59 <UCard> 62 - <template #header>{{ wallet.Name }}</template> 63 - <template #default>${{ wallet.Balance }}</template> 60 + <template #header> 61 + {{ wallet.Name }} 62 + </template> 63 + <template #default> Current Balance: ${{ wallet.Balance }} </template> 64 64 </UCard> 65 65 </a> 66 66 </template>
-11
frontend/src/routes/wallets/wallet/+wallet-config.ts
··· 1 - import { createRouteBuilder } from '@jeffydc/ruta-vue'; 2 - 3 - import { parentRoute } from './+route.gen.ts'; 4 - 5 - export const route = createRouteBuilder(parentRoute, ':walletId') 6 - .layout({ 7 - parseParams(params) { 8 - return { walletId: +params.walletId }; 9 - }, 10 - }) 11 - .page();
-3
frontend/src/routes/wallets/wallet/+wallet-error.vue
··· 1 - <script setup lang="ts"></script> 2 - 3 - <template></template>
-3
frontend/src/routes/wallets/wallet/+wallet-layout.vue
··· 1 - <script setup lang="ts"></script> 2 - 3 - <template></template>
-3
frontend/src/routes/wallets/wallet/+wallet-page.vue
··· 1 - <script setup lang="ts"></script> 2 - 3 - <template></template>
+13
frontend/src/routes/wallets/walletID/+walletID-config.ts
··· 1 + import { createRouteBuilder } from '@jeffydc/ruta-vue'; 2 + 3 + import { walletQueryOpts } from '../../../api/wallet.ts'; 4 + import { parentRoute } from './+route.gen.ts'; 5 + 6 + export const route = createRouteBuilder(parentRoute, ':walletID').page({ 7 + parseParams(params) { 8 + return { walletID: +params.walletID }; 9 + }, 10 + load: async ({ context, to }) => { 11 + await context.qc.ensureQueryData(walletQueryOpts(to.params.walletID)); 12 + }, 13 + });
+83
frontend/src/routes/wallets/walletID/+walletID-page.vue
··· 1 + <script setup lang="ts"> 2 + import { shallowReactive, shallowRef, toRef } from 'vue'; 3 + import { useNewTransaction, useTransactions, useWallet } from '../../../api/wallet'; 4 + import { RouteTyped } from './+route.gen'; 5 + 6 + const dialogOpened = shallowRef(false); 7 + const transactionForm = shallowReactive({ 8 + reason: '', 9 + amount: 0, 10 + type: '', 11 + walletID: -1, 12 + }); 13 + 14 + const route = RouteTyped.usePageRoute(); 15 + const walletID = toRef(() => route.params.walletID); 16 + const wallet = useWallet(walletID); 17 + const transactions = useTransactions(walletID); 18 + const newTransaction = useNewTransaction(); 19 + 20 + function handleNewTransaction() { 21 + transactionForm.walletID = route.params.walletID; 22 + newTransaction.mutate(transactionForm, { 23 + onSuccess: () => { 24 + transactionForm.amount = 0; 25 + transactionForm.reason = ''; 26 + transactionForm.type = ''; 27 + dialogOpened.value = false; 28 + }, 29 + }); 30 + } 31 + </script> 32 + 33 + <template> 34 + <UModal title="Create a Transaction" v-model:open="dialogOpened"> 35 + <UButton class="ml-auto">Create a Transaction</UButton> 36 + 37 + <template #body> 38 + <UForm class="flex flex-col gap-4" :state="transactionForm" @submit="handleNewTransaction"> 39 + <UFormField label="Reason" name="reason"> 40 + <UInput 41 + type="text" 42 + placeholder="Breakfast" 43 + class="w-full" 44 + v-model="transactionForm.reason" 45 + /> 46 + </UFormField> 47 + 48 + <UFormField label="Amount" name="amount"> 49 + <UInput type="number" class="w-full" v-model="transactionForm.amount" :min="30" /> 50 + </UFormField> 51 + 52 + <UFormField label="Type" name="type"> 53 + <URadioGroup v-model="transactionForm.type" :items="['Income', 'Outcome']" /> 54 + </UFormField> 55 + 56 + <UButton class="self-end" :loading="newTransaction.isPending" type="submit">Create</UButton> 57 + 58 + <UAlert v-if="newTransaction.isError" color="error" variant="subtle"> 59 + <template #title> 60 + {{ newTransaction.error }} 61 + </template> 62 + </UAlert> 63 + </UForm> 64 + </template> 65 + </UModal> 66 + 67 + <UCard v-if="wallet.isSuccess"> 68 + <template #header> 69 + {{ wallet.data.Name }} 70 + </template> 71 + <template #default> 72 + Current Balance: ${{ wallet.data.Balance }} 73 + 74 + <div class="flex flex-col gap-4 mt-8"> 75 + <h3 class="text-lg">Transactions</h3> 76 + <UCard v-for="tx in transactions.data" :key="tx.ID"> 77 + <template #header> {{ tx.Reason }} | {{ tx.Type }} </template> 78 + <template #default> {{ tx.Amount }} | {{ tx.CreatedAt }} </template> 79 + </UCard> 80 + </div> 81 + </template> 82 + </UCard> 83 + </template>