Personal finance tracker
0
fork

Configure Feed

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

wip: initial atproto oauth integration

+316 -112
-1
backend/.env.example
··· 1 - PORT=
+9
backend/config.json
··· 1 + { 2 + "APP_ENV": "development", 3 + "APP_ADDR": "localhost:50837", 4 + "CLIENT_NAME": "Subete", 5 + "CLIENT_URL": "http://localhost:50837", 6 + "CLIENT_SECRET": "z42twLj2gZeJSeRgZ4yPyEb6Yg6nawhU2W8y2ETDDFFyvwym", 7 + "DB_NAME": "subete.db", 8 + "SESSION_SECRET": "c727506476c23ea840e45fb32edf51fc6c34020ca2b373c3c88038f031bbead5" 9 + }
+18 -15
backend/go.mod
··· 3 3 go 1.25 4 4 5 5 require ( 6 + github.com/bluesky-social/indigo v0.0.0-20260220055544-bf41e2ee75ab 6 7 github.com/danielgtaylor/huma/v2 v2.35.0 7 8 github.com/go-chi/chi/v5 v5.2.5 8 - github.com/go-playground/validator/v10 v10.30.1 9 9 github.com/spf13/cobra v1.9.1 10 - github.com/spf13/viper v1.21.0 11 10 github.com/uptrace/bun v1.2.16 12 11 github.com/uptrace/bun/dialect/sqlitedialect v1.2.16 13 12 github.com/uptrace/bun/driver/sqliteshim v1.2.16 ··· 15 14 ) 16 15 17 16 require ( 17 + github.com/beorn7/perks v1.0.1 // indirect 18 + github.com/cespare/xxhash/v2 v2.2.0 // indirect 18 19 github.com/dustin/go-humanize v1.0.1 // indirect 20 + github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect 19 21 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 22 + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 23 + github.com/google/go-querystring v1.1.0 // indirect 25 24 github.com/google/uuid v1.6.0 // indirect 25 + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 26 26 github.com/inconshreveable/mousetrap v1.1.0 // indirect 27 27 github.com/jinzhu/inflection v1.0.0 // indirect 28 - github.com/leodido/go-urn v1.4.0 // indirect 29 28 github.com/mattn/go-colorable v0.1.14 // indirect 30 29 github.com/mattn/go-isatty v0.0.20 // indirect 31 30 github.com/mattn/go-sqlite3 v1.14.32 // indirect 31 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect 32 + github.com/mr-tron/base58 v1.2.0 // indirect 32 33 github.com/ncruces/go-strftime v1.0.0 // indirect 33 - github.com/pelletier/go-toml/v2 v2.2.4 // indirect 34 + github.com/prometheus/client_golang v1.17.0 // indirect 35 + github.com/prometheus/client_model v0.5.0 // indirect 36 + github.com/prometheus/common v0.45.0 // indirect 37 + github.com/prometheus/procfs v0.12.0 // indirect 34 38 github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect 35 39 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 40 github.com/spf13/pflag v1.0.10 // indirect 41 - github.com/subosito/gotenv v1.6.0 // indirect 41 + github.com/stretchr/testify v1.11.1 // indirect 42 42 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect 43 43 github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 44 44 github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 45 - go.yaml.in/yaml/v3 v3.0.4 // indirect 45 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 46 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 46 47 golang.org/x/crypto v0.48.0 // indirect 47 48 golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect 48 49 golang.org/x/net v0.50.0 // indirect 49 50 golang.org/x/sys v0.41.0 // indirect 50 51 golang.org/x/text v0.34.0 // indirect 52 + golang.org/x/time v0.3.0 // indirect 53 + google.golang.org/protobuf v1.36.6 // indirect 51 54 modernc.org/libc v1.67.0 // indirect 52 55 modernc.org/mathutil v1.7.1 // indirect 53 56 modernc.org/memory v1.11.0 // indirect
+58 -44
backend/go.sum
··· 1 + github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 2 + github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 3 + github.com/bluesky-social/indigo v0.0.0-20260220055544-bf41e2ee75ab h1:Cs35T2tAN3Q6mMH5mBaY09nmCNOn/GkZS1F7jfMxlR8= 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= 1 7 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 2 8 github.com/danielgtaylor/huma/v2 v2.35.0 h1:FRg3FgVKcMogVhbNY7FjyTwk+p/orLBR3hQBvXXg7dw= 3 9 github.com/danielgtaylor/huma/v2 v2.35.0/go.mod h1:3elp5brzdyyZsPlDVvf6w8RLnklKp3abolr+5op3fP0= ··· 5 11 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 12 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 7 13 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 14 + github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg= 15 + github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw= 8 16 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 9 17 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 18 github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= 17 19 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= 20 + github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 21 + github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 22 + github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 28 23 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 29 24 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 25 + github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 26 + github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 30 27 github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= 31 28 github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 32 29 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= ··· 35 32 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 36 33 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 37 34 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 35 + github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= 36 + github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= 38 37 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 39 38 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= 39 + github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= 40 + github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 46 41 github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 47 42 github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 48 43 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 49 44 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 50 45 github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= 51 46 github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 47 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= 48 + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 49 + github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 50 + github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 51 + github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 52 + github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 53 + github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= 54 + github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= 55 + github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= 56 + github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= 57 + github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= 58 + github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= 59 + github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= 60 + github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= 61 + github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= 62 + github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 52 63 github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= 53 64 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 65 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 59 66 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 67 + github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= 68 + github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 69 + github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= 70 + github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= 71 + github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= 72 + github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= 73 + github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= 74 + github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 60 75 github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= 61 76 github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 62 77 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 63 78 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 79 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= 80 + github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 81 + github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 75 82 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 76 83 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 77 84 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 78 85 github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 79 86 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 87 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 83 88 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 89 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= 87 90 github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= 88 91 github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= ··· 97 100 github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= 98 101 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 99 102 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= 103 + github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 104 + github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 105 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 106 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 107 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 108 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 102 109 golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= 103 110 golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= 104 111 golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0= ··· 114 121 golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 115 122 golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= 116 123 golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= 124 + golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 125 + golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 117 126 golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= 118 127 golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= 128 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 129 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 130 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 131 + google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 132 + google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 119 133 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 134 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 123 135 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 136 + lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 137 + lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 124 138 modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= 125 139 modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= 126 140 modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
+27 -29
backend/internal/config/config.go
··· 1 1 package config 2 2 3 3 import ( 4 + "encoding/json" 4 5 "os" 5 - 6 - "github.com/go-playground/validator/v10" 7 - "github.com/spf13/viper" 8 6 ) 9 7 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") 8 + type _Config struct { 9 + APP_ENV string 10 + APP_ADDR string 11 + CLIENT_NAME string 12 + CLIENT_URL string 13 + CLIENT_SECRET string 14 + DB_NAME string 15 + SESSION_SECRET string 18 16 19 - v := viper.New() 20 - v.SetConfigFile(".env") 21 - v.SetConfigType("env") 17 + // Computed 18 + DEV bool 19 + PROD bool 20 + } 22 21 23 - if err := v.ReadInConfig(); err != nil { 24 - panic(err) 22 + func load() *_Config { 23 + configFile := "config.json" 24 + if _, err := os.Stat(configFile); os.IsNotExist(err) { 25 + configFile = "../config.json" 25 26 } 26 - 27 - if env == "testing" { 28 - v.SetConfigFile(".env.testing") 29 - if err := v.MergeInConfig(); err != nil { 30 - panic(err) 31 - } 27 + if _, err := os.Stat(configFile); os.IsNotExist(err) { 28 + panic(err) 32 29 } 33 - 34 - var config Config 35 - if err := v.Unmarshal(&config); err != nil { 30 + data, err := os.ReadFile(configFile) 31 + if err != nil { 36 32 panic(err) 37 33 } 38 - 39 - validate := validator.New(validator.WithRequiredStructEnabled()) 40 - if err := validate.Struct(config); err != nil { 34 + var config _Config 35 + if err := json.Unmarshal(data, &config); err != nil { 41 36 panic(err) 42 37 } 43 - 38 + config.DEV = config.APP_ENV == "development" 39 + config.PROD = config.APP_ENV == "production" 44 40 return &config 45 41 } 42 + 43 + var Loaded = load()
+3 -3
backend/internal/db/db.go
··· 16 16 "tangled.org/jeffydc.xyz/subete/backend/internal/db/migrations" 17 17 ) 18 18 19 - func NewDB(cfg *config.Config) *bun.DB { 20 - sqldb, err := sql.Open(sqliteshim.ShimName, cfg.DB_NAME) 19 + func NewDB() *bun.DB { 20 + sqldb, err := sql.Open(sqliteshim.ShimName, config.Loaded.DB_NAME) 21 21 if err != nil { 22 22 panic(err) 23 23 } ··· 25 25 } 26 26 27 27 func newMigrator() *migrate.Migrator { 28 - return migrate.NewMigrator(NewDB(config.Load()), migrations.Migrations) 28 + return migrate.NewMigrator(NewDB(), migrations.Migrations) 29 29 } 30 30 31 31 func AddDBCommands(cli humacli.CLI) {
+11 -8
backend/main.go
··· 15 15 16 16 "tangled.org/jeffydc.xyz/subete/backend/internal/config" 17 17 "tangled.org/jeffydc.xyz/subete/backend/internal/db" 18 + "tangled.org/jeffydc.xyz/subete/backend/oauth" 18 19 "tangled.org/jeffydc.xyz/subete/backend/wallet" 19 20 ) 20 21 ··· 22 23 23 24 func main() { 24 25 cli := humacli.New(func(hooks humacli.Hooks, options *Options) { 25 - // Application config 26 - config := config.Load() 27 - 28 26 // DB 29 - bundb := db.NewDB(config) 27 + bundb := db.NewDB() 30 28 bundb.AddQueryHook(bundebug.NewQueryHook( 31 29 bundebug.FromEnv(), 32 30 )) ··· 42 40 {URL: "/api"}, 43 41 } 44 42 api := humachi.New(r, humaconfig) 45 - // user.New(config, bundb).Register(api) 46 - wallet.New(config, bundb).Register(api) 43 + 44 + // Unauthed 45 + oauth.New(bundb).Register(api) 46 + 47 + // Authed 48 + // user.New(bundb).Register(api) 49 + wallet.New(bundb).Register(api) 47 50 }) 48 51 49 52 // Server 50 53 server := http.Server{ 51 - Addr: config.APP_ADDR, 54 + Addr: config.Loaded.APP_ADDR, 52 55 Handler: r, 53 56 } 54 57 hooks.OnStart(func() { 55 - fmt.Println("server at:", config.APP_ADDR) 58 + fmt.Println(config.Loaded.APP_ENV, "server listening at:", config.Loaded.CLIENT_URL) 56 59 server.ListenAndServe() 57 60 }) 58 61 hooks.OnStop(func() {
+78
backend/oauth/oauth.go
··· 1 + package oauth 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + 7 + "github.com/bluesky-social/indigo/atproto/atcrypto" 8 + atoauth "github.com/bluesky-social/indigo/atproto/auth/oauth" 9 + "github.com/danielgtaylor/huma/v2" 10 + "github.com/uptrace/bun" 11 + "tangled.org/jeffydc.xyz/subete/backend/internal/config" 12 + ) 13 + 14 + type OAuth struct { 15 + clientApp *atoauth.ClientApp 16 + jwksURL string 17 + } 18 + 19 + func New(db *bun.DB) *OAuth { 20 + scopes := []string{"atproto"} 21 + callbackURL := config.Loaded.CLIENT_URL + "/oauth/callback" 22 + 23 + var clientConfig atoauth.ClientConfig 24 + if config.Loaded.DEV { 25 + clientConfig = atoauth.NewLocalhostConfig(callbackURL, scopes) 26 + } else { 27 + clientID := fmt.Sprintf("%s/oauth-client-metadata.json", config.Loaded.CLIENT_URL) 28 + clientConfig = atoauth.NewPublicConfig(clientID, callbackURL, scopes) 29 + } 30 + 31 + priv, err := atcrypto.ParsePrivateMultibase(config.Loaded.CLIENT_SECRET) 32 + if err != nil { 33 + panic(err) 34 + } 35 + if err := clientConfig.SetClientSecret(priv, config.Loaded.CLIENT_URL); err != nil { 36 + panic(err) 37 + } 38 + 39 + store := NewDBStore(db) 40 + clientApp := atoauth.NewClientApp(&clientConfig, store) 41 + 42 + return &OAuth{ 43 + clientApp: clientApp, 44 + jwksURL: fmt.Sprintf("%s/oauth/jwks.json", config.Loaded.CLIENT_URL), 45 + } 46 + } 47 + 48 + func (oauth *OAuth) Register(api huma.API) { 49 + type clientMetadataOutput struct { 50 + Body *atoauth.ClientMetadata 51 + } 52 + huma.Get(api, "/oauth-client-metadata.json", func(ctx context.Context, input *struct{}) (*clientMetadataOutput, error) { 53 + meta := oauth.clientApp.Config.ClientMetadata() 54 + meta.JWKSURI = &oauth.jwksURL 55 + meta.ClientName = &config.Loaded.CLIENT_NAME 56 + meta.ClientURI = &config.Loaded.CLIENT_URL 57 + 58 + err := meta.Validate(oauth.clientApp.Config.ClientID) 59 + if config.Loaded.PROD && err != nil { 60 + return nil, err 61 + } 62 + resp := &clientMetadataOutput{Body: &meta} 63 + return resp, nil 64 + }) 65 + 66 + type jwksOutput struct { 67 + Body *atoauth.JWKS 68 + } 69 + huma.Get(api, "/oauth/jwks.json", func(ctx context.Context, input *struct{}) (*jwksOutput, error) { 70 + jwks := oauth.clientApp.Config.PublicJWKS() 71 + resp := &jwksOutput{Body: &jwks} 72 + return resp, nil 73 + }) 74 + 75 + huma.Get(api, "/oauth/callback", func(ctx context.Context, i *struct{}) (*struct{}, error) { 76 + return nil, nil 77 + }) 78 + }
+106
backend/oauth/store.go
··· 1 + package oauth 2 + 3 + import ( 4 + "context" 5 + "time" 6 + 7 + atoauth "github.com/bluesky-social/indigo/atproto/auth/oauth" 8 + "github.com/bluesky-social/indigo/atproto/syntax" 9 + "github.com/uptrace/bun" 10 + ) 11 + 12 + type SessionModel struct { 13 + bun.BaseModel `bun:"table:sessions"` 14 + 15 + accountDid syntax.DID `bun:"account_did,pk"` 16 + sessionId string `bun:"session_id,pk"` 17 + data atoauth.ClientSessionData `bun:"data,json"` 18 + createdAt time.Time `bun:"created_at,index"` 19 + updatedAt time.Time `bun:"updated_at,index"` 20 + } 21 + 22 + type AuthRequestModel struct { 23 + bun.BaseModel `bun:"table:auth_reqs"` 24 + 25 + state string `bun:"state,pk"` 26 + data atoauth.AuthRequestData `bun:"data,json"` 27 + created_at time.Time `bun:"created_at,index"` 28 + } 29 + 30 + type storeConfig struct { 31 + // The purpose of these limits is to avoid dead sessions hanging around in the db indefinitely. 32 + // The durations here should be *at least as long as* the expected duration of the oauth session itself. 33 + SessionExpiryDuration time.Duration // duration since session creation 34 + SessionInactivityDuration time.Duration // duration since last session update 35 + AuthRequestExpiryDuration time.Duration // duration since auth request creation 36 + } 37 + 38 + type dbStore struct { 39 + db *bun.DB 40 + config *storeConfig 41 + } 42 + 43 + var _ atoauth.ClientAuthStore = &dbStore{} 44 + 45 + func NewDBStore(db *bun.DB) *dbStore { 46 + return &dbStore{ 47 + db: db, 48 + config: &storeConfig{ 49 + SessionExpiryDuration: time.Hour * 24 * 90, 50 + SessionInactivityDuration: time.Hour * 24 * 14, 51 + AuthRequestExpiryDuration: time.Minute * 30, 52 + }, 53 + } 54 + } 55 + 56 + func (store *dbStore) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*atoauth.ClientSessionData, error) { 57 + expiry_threshold := time.Now().Add(-store.config.SessionExpiryDuration) 58 + inactive_threshold := time.Now().Add(-store.config.SessionInactivityDuration) 59 + _, err := store.db.NewDelete().Model(&AuthRequestModel{}).Where("created_at < ? OR updated_at < ?", expiry_threshold, inactive_threshold).Exec(ctx) 60 + if err != nil { 61 + return nil, err 62 + } 63 + 64 + model := &SessionModel{accountDid: did, sessionId: sessionID} 65 + err = store.db.NewSelect().Model(model).Scan(ctx) 66 + if err != nil { 67 + return nil, err 68 + } 69 + return &model.data, err 70 + } 71 + 72 + func (store *dbStore) SaveSession(ctx context.Context, sess atoauth.ClientSessionData) error { 73 + session := &SessionModel{accountDid: sess.AccountDID, sessionId: sess.SessionID} 74 + _, err := store.db.NewInsert().Model(session).Exec(ctx) 75 + return err 76 + } 77 + 78 + func (store *dbStore) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error { 79 + session := &SessionModel{accountDid: did, sessionId: sessionID} 80 + _, err := store.db.NewDelete().Model(session).Exec(ctx) 81 + return err 82 + } 83 + 84 + func (store *dbStore) GetAuthRequestInfo(ctx context.Context, state string) (*atoauth.AuthRequestData, error) { 85 + threshold := time.Now().Add(-store.config.AuthRequestExpiryDuration) 86 + if _, err := store.db.NewDelete().Model(&AuthRequestModel{}).Where("created_at < ?", threshold).Exec(ctx); err != nil { 87 + return nil, err 88 + } 89 + 90 + model := &AuthRequestModel{} 91 + err := store.db.NewSelect().Model(model).Where("state = ?", state).Scan(ctx) 92 + if err != nil { 93 + return nil, err 94 + } 95 + return &model.data, err 96 + } 97 + 98 + func (store *dbStore) SaveAuthRequestInfo(ctx context.Context, info atoauth.AuthRequestData) error { 99 + _, err := store.db.NewInsert().Model(&AuthRequestModel{state: info.State, data: info}).Exec(ctx) 100 + return err 101 + } 102 + 103 + func (store *dbStore) DeleteAuthRequestInfo(ctx context.Context, state string) error { 104 + _, err := store.db.NewDelete().Model(&AuthRequestModel{state: state}).Exec(ctx) 105 + return err 106 + }
+3 -6
backend/user/user_router.go
··· 8 8 "github.com/danielgtaylor/huma/v2" 9 9 "github.com/go-chi/chi/v5" 10 10 "github.com/uptrace/bun" 11 - "tangled.org/jeffydc.xyz/subete/backend/internal/config" 12 11 ) 13 12 14 13 type UserDep struct { 15 - config *config.Config 16 - db *bun.DB 14 + db *bun.DB 17 15 } 18 16 19 - func New(config *config.Config, db *bun.DB) *UserDep { 17 + func New(db *bun.DB) *UserDep { 20 18 return &UserDep{ 21 - config: config, 22 - db: db, 19 + db: db, 23 20 } 24 21 } 25 22
+3 -6
backend/wallet/wallet_router.go
··· 5 5 6 6 "github.com/danielgtaylor/huma/v2" 7 7 "github.com/uptrace/bun" 8 - "tangled.org/jeffydc.xyz/subete/backend/internal/config" 9 8 ) 10 9 11 10 type handler struct { 12 - config *config.Config 13 - db *bun.DB 11 + db *bun.DB 14 12 } 15 13 16 14 type walletIdInput struct { 17 15 ID uint64 `path:"walletId"` 18 16 } 19 17 20 - func New(config *config.Config, db *bun.DB) *handler { 18 + func New(db *bun.DB) *handler { 21 19 return &handler{ 22 - config: config, 23 - db: db, 20 + db: db, 24 21 } 25 22 } 26 23