From a5238921e339bc9925476159cd3f9b112bcb2b48 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Wed, 22 Apr 2026 21:05:50 +0200 Subject: [PATCH] config loaded from .env --- .gitignore | 5 + Cargo.lock | 16 ++ Cargo.toml | 7 + Makefile | 17 --- cmd/api/main.go | 40 ----- go.mod | 57 -------- go.sum | 138 ------------------ internal/config/config.go | 65 --------- internal/db/db.go | 39 ----- internal/db/migration.go | 33 ----- internal/db/users/db.go | 32 ---- internal/db/users/models.go | 19 --- internal/db/users/querier.go | 20 --- internal/db/users/queries.sql | 19 --- internal/db/users/queries.sql.go | 85 ----------- internal/http/api/auth/handler.go | 91 ------------ internal/http/api/health/handler.go | 42 ------ internal/http/router.go | 15 -- internal/http/server.go | 43 ------ internal/jwt/jwt.go | 68 --------- internal/logger/logger.go | 50 ------- internal/password/password.go | 15 -- internal/service/users/user_service.go | 123 ---------------- .../20260419141635_create_users_table.sql | 11 -- migrations/embed.go | 6 - sqlc.yaml | 31 ---- src/config.rs | 17 +++ src/main.rs | 6 + 28 files changed, 51 insertions(+), 1059 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml delete mode 100644 Makefile delete mode 100644 cmd/api/main.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 internal/config/config.go delete mode 100644 internal/db/db.go delete mode 100644 internal/db/migration.go delete mode 100644 internal/db/users/db.go delete mode 100644 internal/db/users/models.go delete mode 100644 internal/db/users/querier.go delete mode 100644 internal/db/users/queries.sql delete mode 100644 internal/db/users/queries.sql.go delete mode 100644 internal/http/api/auth/handler.go delete mode 100644 internal/http/api/health/handler.go delete mode 100644 internal/http/router.go delete mode 100644 internal/http/server.go delete mode 100644 internal/jwt/jwt.go delete mode 100644 internal/logger/logger.go delete mode 100644 internal/password/password.go delete mode 100644 internal/service/users/user_service.go delete mode 100644 migrations/20260419141635_create_users_table.sql delete mode 100644 migrations/embed.go delete mode 100644 sqlc.yaml create mode 100644 src/config.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 1c1dfaa..55ca38d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ target/ .env .data/ + + +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6ffaa47 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "rhythm-backend" +version = "0.1.0" +dependencies = [ + "dotenvy", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e84f65f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rhythm-backend" +version = "0.1.0" +edition = "2024" + +[dependencies] +dotenvy = "0.15.7" diff --git a/Makefile b/Makefile deleted file mode 100644 index 5ef415e..0000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: help run test fmt - -help: - @echo "Targets:" - @echo " run - run API" - @echo " test - run tests" - @echo " fmt - format Go code" - -run: - go run ./cmd/api - -test: - go test ./... - -fmt: - go fmt ./... - diff --git a/cmd/api/main.go b/cmd/api/main.go deleted file mode 100644 index 0c66e3b..0000000 --- a/cmd/api/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - "git.kanopo.dev/rhythm/rhythm-backend/internal/db" - usersdb "git.kanopo.dev/rhythm/rhythm-backend/internal/db/users" - "git.kanopo.dev/rhythm/rhythm-backend/internal/http" - "git.kanopo.dev/rhythm/rhythm-backend/internal/http/api/auth" - "git.kanopo.dev/rhythm/rhythm-backend/internal/http/api/health" - "git.kanopo.dev/rhythm/rhythm-backend/internal/logger" - "git.kanopo.dev/rhythm/rhythm-backend/internal/service/users" - "go.uber.org/fx" - "go.uber.org/fx/fxevent" - "go.uber.org/zap" - - "github.com/jackc/pgx/v5/pgxpool" -) - -func main() { - fx.New( - fx.Provide( - config.Provide, - logger.ProvideLogger, - db.ProvidePool, - func(pool *pgxpool.Pool) usersdb.Querier { - return usersdb.New(pool) - }, - users.NewService, - http.NewServer, - health.NewHandler, - auth.NewHandler, - ), - fx.Invoke( - http.GlueRoutes, - ), - fx.WithLogger(func(logger *zap.Logger) fxevent.Logger { - return &fxevent.ZapLogger{Logger: logger} - }), - ).Run() -} diff --git a/go.mod b/go.mod deleted file mode 100644 index bfaf769..0000000 --- a/go.mod +++ /dev/null @@ -1,57 +0,0 @@ -module git.kanopo.dev/rhythm/rhythm-backend - -go 1.26.2 - -require ( - github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/google/uuid v1.6.0 - github.com/jackc/pgx/v5 v5.9.2 - github.com/joho/godotenv v1.5.1 - github.com/pressly/goose/v3 v3.27.0 - golang.org/x/crypto v0.48.0 -) - -require ( - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.15.0 // indirect - github.com/bytedance/sonic/loader v0.5.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.12 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.30.1 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/goccy/go-yaml v1.19.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.59.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.1 // indirect - go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect - go.uber.org/dig v1.19.0 // indirect - golang.org/x/arch v0.22.0 // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/sys v0.41.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect -) - -require ( - github.com/gin-gonic/gin v1.12.0 - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/mfridman/interpolate v0.0.2 // indirect - github.com/sethvargo/go-retry v0.3.0 // indirect - go.uber.org/fx v1.24.0 - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.1 - golang.org/x/sync v0.19.0 // indirect - golang.org/x/text v0.34.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index ac369b4..0000000 --- a/go.sum +++ /dev/null @@ -1,138 +0,0 @@ -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= -github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= -github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= -github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= -github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= -github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= -github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= -github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= -github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= -github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= -github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= -github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pressly/goose/v3 v3.27.0 h1:/D30gVTuQhu0WsNZYbJi4DMOsx1lNq+6SkLe+Wp59BM= -github.com/pressly/goose/v3 v3.27.0/go.mod h1:3ZBeCXqzkgIRvrEMDkYh1guvtoJTU5oMMuDdkutoM78= -github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= -github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= -github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= -github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= -go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= -go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= -go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= -go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= -go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= -golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= -modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= -modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= -modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= -modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= -modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= diff --git a/internal/config/config.go b/internal/config/config.go deleted file mode 100644 index 6beca9f..0000000 --- a/internal/config/config.go +++ /dev/null @@ -1,65 +0,0 @@ -package config - -import ( - "fmt" - _ "github.com/joho/godotenv/autoload" - "log" - "os" - "time" -) - -type Config struct { - AppEnv string - DbUrl string - ServerPort string - JWTSecret string - JWTExpiry time.Duration - RefreshExpiry time.Duration -} - -func Load() Config { - var dbUrl string - { - username := getEnv("DB_USERNAME") - password := getEnv("DB_PASSWORD") - name := getEnv("DB_NAME") - port := getEnv("DB_PORT") - host := getEnv("DB_HOST") - - dbUrl = fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=disable", username, password, host, port, name) - } - - appEnv := os.Getenv("APP_ENV") - if appEnv == "" { - appEnv = "development" - } - - jwtSecret := getEnv("JWT_SECRET") - if jwtSecret == "" { - log.Fatal("JWT_SECRET env variable is required") - } - - cfg := Config{ - AppEnv: appEnv, - DbUrl: dbUrl, - ServerPort: "8080", - JWTSecret: jwtSecret, - JWTExpiry: time.Minute * 15, - RefreshExpiry: time.Hour * 24 * 7, - } - - return cfg -} - -func getEnv(key string) string { - v := os.Getenv(key) - if v == "" { - log.Fatalf("The env variable %v is not defined and the application can not operate without\n", key) - } - return v -} - -func Provide() *Config { - cfg := Load() - return &cfg -} diff --git a/internal/db/db.go b/internal/db/db.go deleted file mode 100644 index 892cf4f..0000000 --- a/internal/db/db.go +++ /dev/null @@ -1,39 +0,0 @@ -package db - -import ( - "context" - "time" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - "github.com/jackc/pgx/v5/pgxpool" - "go.uber.org/fx" - "go.uber.org/zap" -) - -func ProvidePool(lc fx.Lifecycle, cfg *config.Config, log *zap.SugaredLogger) (*pgxpool.Pool, error) { - ctx := context.Background() - pool, err := pgxpool.New(ctx, cfg.DbUrl) - if err != nil { - return nil, err - } - - { - ctx, cancel := context.WithTimeout(ctx, time.Second*5) - defer cancel() - if err := pool.Ping(ctx); err != nil { - return nil, err - } - log.Info("successfully connected to database") - RunMigrations(cfg.DbUrl, log) - } - - lc.Append(fx.Hook{ - OnStop: func(ctx context.Context) error { - log.Info("Closing database pool") - pool.Close() - return nil - }, - }) - - return pool, nil -} diff --git a/internal/db/migration.go b/internal/db/migration.go deleted file mode 100644 index 1ef36ae..0000000 --- a/internal/db/migration.go +++ /dev/null @@ -1,33 +0,0 @@ -package db - -import ( - "database/sql" - "log" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/logger" - "git.kanopo.dev/rhythm/rhythm-backend/migrations" - _ "github.com/jackc/pgx/v5/stdlib" - "github.com/pressly/goose/v3" - "go.uber.org/zap" -) - -func RunMigrations(dbURL string, zLog *zap.SugaredLogger) { - db, err := sql.Open("pgx", dbURL) - if err != nil { - log.Fatalf("open db for migrations: %v", err.Error()) - } - defer db.Close() - - goose.SetLogger(&logger.GooseLogger{SugaredLogger: zLog}) - - // 2. Pass the exported FS to Goose! - goose.SetBaseFS(migrations.FS) - if err := goose.SetDialect("postgres"); err != nil { - log.Fatalf("set goose dialect: %v", err.Error()) - } - - // 3. Run the migrations using the current directory "." of the embed.FS - if err := goose.Up(db, "."); err != nil { - log.Fatalf("run goose up: %v", err.Error()) - } -} diff --git a/internal/db/users/db.go b/internal/db/users/db.go deleted file mode 100644 index 4ba7c7e..0000000 --- a/internal/db/users/db.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.30.0 - -package usersdb - -import ( - "context" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgconn" -) - -type DBTX interface { - Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) - Query(context.Context, string, ...interface{}) (pgx.Rows, error) - QueryRow(context.Context, string, ...interface{}) pgx.Row -} - -func New(db DBTX) *Queries { - return &Queries{db: db} -} - -type Queries struct { - db DBTX -} - -func (q *Queries) WithTx(tx pgx.Tx) *Queries { - return &Queries{ - db: tx, - } -} diff --git a/internal/db/users/models.go b/internal/db/users/models.go deleted file mode 100644 index 8d93028..0000000 --- a/internal/db/users/models.go +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.30.0 - -package usersdb - -import ( - "time" - - "github.com/google/uuid" -) - -type User struct { - ID uuid.UUID - Email string - Password string - CreatedAt time.Time - UpdatedAt time.Time -} diff --git a/internal/db/users/querier.go b/internal/db/users/querier.go deleted file mode 100644 index 2c673d9..0000000 --- a/internal/db/users/querier.go +++ /dev/null @@ -1,20 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.30.0 - -package usersdb - -import ( - "context" - - "github.com/google/uuid" -) - -type Querier interface { - CreateUser(ctx context.Context, arg CreateUserParams) (User, error) - DeleteUser(ctx context.Context, id uuid.UUID) error - GetUser(ctx context.Context, id uuid.UUID) (User, error) - GetUserByEmail(ctx context.Context, email string) (User, error) -} - -var _ Querier = (*Queries)(nil) diff --git a/internal/db/users/queries.sql b/internal/db/users/queries.sql deleted file mode 100644 index 4d0f857..0000000 --- a/internal/db/users/queries.sql +++ /dev/null @@ -1,19 +0,0 @@ --- name: GetUser :one -select * from users -where id = $1 limit 1; - --- name: GetUserByEmail :one -select * from users -where email = $1 limit 1; - --- name: CreateUser :one -insert into users ( - email, password -) values ( - $1, $2 -) -returning *; - --- name: DeleteUser :exec -DELETE FROM users -WHERE id = $1; diff --git a/internal/db/users/queries.sql.go b/internal/db/users/queries.sql.go deleted file mode 100644 index c1ecb72..0000000 --- a/internal/db/users/queries.sql.go +++ /dev/null @@ -1,85 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.30.0 -// source: queries.sql - -package usersdb - -import ( - "context" - - "github.com/google/uuid" -) - -const createUser = `-- name: CreateUser :one -insert into users ( - email, password -) values ( - $1, $2 -) -returning id, email, password, created_at, updated_at -` - -type CreateUserParams struct { - Email string - Password string -} - -func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) { - row := q.db.QueryRow(ctx, createUser, arg.Email, arg.Password) - var i User - err := row.Scan( - &i.ID, - &i.Email, - &i.Password, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const deleteUser = `-- name: DeleteUser :exec -DELETE FROM users -WHERE id = $1 -` - -func (q *Queries) DeleteUser(ctx context.Context, id uuid.UUID) error { - _, err := q.db.Exec(ctx, deleteUser, id) - return err -} - -const getUser = `-- name: GetUser :one -select id, email, password, created_at, updated_at from users -where id = $1 limit 1 -` - -func (q *Queries) GetUser(ctx context.Context, id uuid.UUID) (User, error) { - row := q.db.QueryRow(ctx, getUser, id) - var i User - err := row.Scan( - &i.ID, - &i.Email, - &i.Password, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getUserByEmail = `-- name: GetUserByEmail :one -select id, email, password, created_at, updated_at from users -where email = $1 limit 1 -` - -func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) { - row := q.db.QueryRow(ctx, getUserByEmail, email) - var i User - err := row.Scan( - &i.ID, - &i.Email, - &i.Password, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} diff --git a/internal/http/api/auth/handler.go b/internal/http/api/auth/handler.go deleted file mode 100644 index 361f102..0000000 --- a/internal/http/api/auth/handler.go +++ /dev/null @@ -1,91 +0,0 @@ -package auth - -import ( - "errors" - "net/http" - - "github.com/gin-gonic/gin" - "go.uber.org/zap" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - "git.kanopo.dev/rhythm/rhythm-backend/internal/service/users" -) - -type Handler struct { - svc users.Service - log *zap.SugaredLogger - cfg *config.Config -} - -func NewHandler(svc users.Service, log *zap.SugaredLogger, cfg *config.Config) *Handler { - return &Handler{ - svc: svc, - log: log, - cfg: cfg, - } -} - -func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) { - rg.POST("/login", h.Login) - rg.POST("/register", h.Register) -} - -func setRefreshTokenCookie(c *gin.Context, token string, maxAge int) { - c.SetCookie( - "refresh_token", - token, - maxAge, - "/", - "", - false, - true, - ) -} - -func (h *Handler) Register(c *gin.Context) { - var req struct { - Email string `json:"email" binding:"required,email"` - Password string `json:"password" binding:"required,min=8"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - result, err := h.svc.Register(c.Request.Context(), req.Email, req.Password) - if err != nil { - h.log.Error("registration error", "error", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) - return - } - - setRefreshTokenCookie(c, result.RefreshToken, int(h.cfg.RefreshExpiry)) - c.JSON(http.StatusCreated, gin.H{"accessToken": result.AccessToken}) -} - -func (h *Handler) Login(c *gin.Context) { - var req struct { - Email string `json:"email" binding:"required,email"` - Password string `json:"password" binding:"required"` - } - - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - result, err := h.svc.Login(c.Request.Context(), req.Email, req.Password) - if err != nil { - if errors.Is(err, users.ErrInvalidCredentials) { - c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"}) - return - } - h.log.Error("login error", "error", err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) - return - } - - setRefreshTokenCookie(c, result.RefreshToken, int(h.cfg.RefreshExpiry)) - c.JSON(http.StatusOK, gin.H{"accessToken": result.AccessToken}) -} diff --git a/internal/http/api/health/handler.go b/internal/http/api/health/handler.go deleted file mode 100644 index 364f819..0000000 --- a/internal/http/api/health/handler.go +++ /dev/null @@ -1,42 +0,0 @@ -package health - -import ( - "context" - "net/http" - - "github.com/gin-gonic/gin" - "github.com/jackc/pgx/v5/pgxpool" - "go.uber.org/zap" -) - -type Handler struct { - log *zap.SugaredLogger - pool *pgxpool.Pool -} - -func NewHandler(log *zap.SugaredLogger, pool *pgxpool.Pool) *Handler { - return &Handler{ - log: log, - pool: pool, - } -} - -func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) { - rg.GET("/live", h.Live) - rg.GET("/ready", h.Ready) -} - -func (h *Handler) Live(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"status": "alive"}) -} - -func (h *Handler) Ready(c *gin.Context) { - ctx := context.Background() - if err := h.pool.Ping(ctx); err != nil { - h.log.Warnw("health check: db ping failed", "error", err) - c.JSON(http.StatusServiceUnavailable, gin.H{"status": "not ready", "error": "db unreachable"}) - return - } - - c.JSON(http.StatusOK, gin.H{"status": "ready"}) -} diff --git a/internal/http/router.go b/internal/http/router.go deleted file mode 100644 index f03426c..0000000 --- a/internal/http/router.go +++ /dev/null @@ -1,15 +0,0 @@ -package http - -import ( - "git.kanopo.dev/rhythm/rhythm-backend/internal/http/api/auth" - "git.kanopo.dev/rhythm/rhythm-backend/internal/http/api/health" - "github.com/gin-gonic/gin" -) - -func GlueRoutes(r *gin.Engine, healthHandler *health.Handler, authHandler *auth.Handler) { - api := r.Group("/api") - healthHandler.RegisterRoutes(api.Group("/health")) - - v1 := api.Group("/v1") - authHandler.RegisterRoutes(v1.Group("/auth")) -} diff --git a/internal/http/server.go b/internal/http/server.go deleted file mode 100644 index 15abd9a..0000000 --- a/internal/http/server.go +++ /dev/null @@ -1,43 +0,0 @@ -package http - -import ( - "context" - "net/http" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - "github.com/gin-gonic/gin" - "go.uber.org/fx" - "go.uber.org/zap" -) - -func NewServer(lc fx.Lifecycle, cfg *config.Config, log *zap.SugaredLogger) *gin.Engine { - if cfg.AppEnv == "production" { - gin.SetMode(gin.ReleaseMode) - } - - r := gin.New() - r.Use(gin.Recovery()) - - server := &http.Server{ - Addr: ":" + cfg.ServerPort, - Handler: r, - } - - lc.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - log.Infof("Starting HTTP server on port %v", cfg.ServerPort) - go func() { - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("error starting http server %v", err.Error()) - } - }() - return nil - }, - OnStop: func(ctx context.Context) error { - log.Info("Shutting down HTTP server\n") - return server.Shutdown(ctx) - }, - }) - - return r -} diff --git a/internal/jwt/jwt.go b/internal/jwt/jwt.go deleted file mode 100644 index 8a9e7ce..0000000 --- a/internal/jwt/jwt.go +++ /dev/null @@ -1,68 +0,0 @@ -package jwt - -import ( - "errors" - "time" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - "github.com/golang-jwt/jwt/v5" -) - -type Claims struct { - UserID string `json:"userId"` - jwt.RegisteredClaims -} - -type TokenPair struct { - AccessToken string - RefreshToken string -} - -func GenerateTokenPair(userID string, cfg *config.Config) (TokenPair, error) { - accessToken, err := generateToken(userID, cfg.JWTSecret, cfg.JWTExpiry) - if err != nil { - return TokenPair{}, err - } - - refreshToken, err := generateToken(userID, cfg.JWTSecret, cfg.RefreshExpiry) - if err != nil { - return TokenPair{}, err - } - - return TokenPair{ - AccessToken: accessToken, - RefreshToken: refreshToken, - }, nil -} - -func generateToken(userID, secret string, expiry time.Duration) (string, error) { - claims := &Claims{ - UserID: userID, - RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiry)), - IssuedAt: jwt.NewNumericDate(time.Now()), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString([]byte(secret)) -} - -func ValidateToken(tokenString, secret string) (*Claims, error) { - token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, errors.New("unexpected signing method") - } - return []byte(secret), nil - }) - - if err != nil { - return nil, err - } - - if claims, ok := token.Claims.(*Claims); ok && token.Valid { - return claims, nil - } - - return nil, errors.New("invalid token") -} diff --git a/internal/logger/logger.go b/internal/logger/logger.go deleted file mode 100644 index 42aa18f..0000000 --- a/internal/logger/logger.go +++ /dev/null @@ -1,50 +0,0 @@ -package logger - -import ( - "log" - - "go.uber.org/zap" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" -) - -func New(env string) (*zap.Logger, *zap.SugaredLogger) { - var zapLogger *zap.Logger - var err error - - if env == "production" { - zapLogger, err = zap.NewProduction() - } else { - zapLogger, err = zap.NewDevelopment() - } - - if err != nil { - log.Fatalf("failed to initialize zap logger: %v", err) - } - - return zapLogger, zapLogger.Sugar() -} - -func ProvideLogger(cfg *config.Config) (*zap.Logger, *zap.SugaredLogger) { - return New(cfg.AppEnv) -} - -type GooseLogger struct { - *zap.SugaredLogger -} - -func (l *GooseLogger) Fatal(v ...interface{}) { - l.SugaredLogger.Fatal(v...) -} - -func (l *GooseLogger) Fatalf(format string, v ...interface{}) { - l.SugaredLogger.Fatalf(format, v...) -} - -func (l *GooseLogger) Print(v ...interface{}) { - l.SugaredLogger.Info(v...) -} - -func (l *GooseLogger) Printf(format string, v ...interface{}) { - l.SugaredLogger.Infof(format, v...) -} diff --git a/internal/password/password.go b/internal/password/password.go deleted file mode 100644 index 447588e..0000000 --- a/internal/password/password.go +++ /dev/null @@ -1,15 +0,0 @@ -package password - -import ( - "golang.org/x/crypto/bcrypt" -) - -func HashPassword(password string) (string, error) { - bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - return string(bytes), err -} - -func CheckPassword(password, hash string) bool { - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} diff --git a/internal/service/users/user_service.go b/internal/service/users/user_service.go deleted file mode 100644 index 1d7ef6c..0000000 --- a/internal/service/users/user_service.go +++ /dev/null @@ -1,123 +0,0 @@ -package users - -import ( - "context" - "errors" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgxpool" - "go.uber.org/zap" - - "git.kanopo.dev/rhythm/rhythm-backend/internal/config" - usersdb "git.kanopo.dev/rhythm/rhythm-backend/internal/db/users" - "git.kanopo.dev/rhythm/rhythm-backend/internal/jwt" - "git.kanopo.dev/rhythm/rhythm-backend/internal/password" -) - -var ErrInvalidCredentials = errors.New("invalid credentials") - -type AuthResult struct { - AccessToken string - RefreshToken string -} - -type Service interface { - Login(ctx context.Context, email, password string) (*AuthResult, error) - Register(ctx context.Context, email, password string) (*AuthResult, error) - GetUserByEmail(ctx context.Context, email string) (usersdb.User, error) -} - -type service struct { - pool *pgxpool.Pool - repo usersdb.Querier - cfg *config.Config - log *zap.SugaredLogger -} - -func NewService(repo usersdb.Querier, cfg *config.Config, log *zap.SugaredLogger, pool *pgxpool.Pool) Service { - return &service{ - repo: repo, - cfg: cfg, - log: log, - pool: pool, - } -} - -func (s *service) Login(ctx context.Context, email, passwordPlain string) (*AuthResult, error) { - user, err := s.repo.GetUserByEmail(ctx, email) - if err != nil { - s.log.Infof("login failed: user not found with email %v", email) - return nil, ErrInvalidCredentials - } - - if !password.CheckPassword(passwordPlain, user.Password) { - s.log.Infof("login failed: invalid password for email %v", email) - return nil, ErrInvalidCredentials - } - - tokenPair, err := jwt.GenerateTokenPair(user.ID.String(), s.cfg) - if err != nil { - s.log.Errorf("failed to generate token pair %v", err) - return nil, err - } - - s.log.Infof("user logged in successfully with email %v", email) - - return &AuthResult{ - AccessToken: tokenPair.AccessToken, - RefreshToken: tokenPair.RefreshToken, - }, nil -} - -func (s *service) Register(ctx context.Context, email, passwordPlain string) (*AuthResult, error) { - tx, err := s.pool.BeginTx(ctx, pgx.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback(ctx) - - txRepo := usersdb.New(tx) - - _, err = txRepo.GetUserByEmail(ctx, email) - if err == nil { - s.log.Infof("registration failed: email already exists %v", email) - return nil, ErrInvalidCredentials - } - if !errors.Is(err, pgx.ErrNoRows) { - return nil, err - } - - hash, err := password.HashPassword(passwordPlain) - if err != nil { - return nil, err - } - - user, err := txRepo.CreateUser(ctx, usersdb.CreateUserParams{ - Email: email, - Password: hash, - }) - if err != nil { - return nil, err - } - - if err := tx.Commit(ctx); err != nil { - return nil, err - } - - tokenPair, err := jwt.GenerateTokenPair(user.ID.String(), s.cfg) - if err != nil { - s.log.Error("failed to generate token pair", "error", err) - return nil, err - } - - s.log.Info("user registered successfully", "email", email) - - return &AuthResult{ - AccessToken: tokenPair.AccessToken, - RefreshToken: tokenPair.RefreshToken, - }, nil -} - -func (s *service) GetUserByEmail(ctx context.Context, email string) (usersdb.User, error) { - return s.repo.GetUserByEmail(ctx, email) -} diff --git a/migrations/20260419141635_create_users_table.sql b/migrations/20260419141635_create_users_table.sql deleted file mode 100644 index 25fac46..0000000 --- a/migrations/20260419141635_create_users_table.sql +++ /dev/null @@ -1,11 +0,0 @@ --- +goose Up -create table users ( - id uuid primary key default uuidv4(), - email varchar(255) not null unique, - password varchar(255) not null, - created_at timestamptz not null default now(), - updated_at timestamptz not null default now() -); - --- +goose Down -drop table if exists users; diff --git a/migrations/embed.go b/migrations/embed.go deleted file mode 100644 index 91cca1c..0000000 --- a/migrations/embed.go +++ /dev/null @@ -1,6 +0,0 @@ -package migrations - -import "embed" - -//go:embed *.sql -var FS embed.FS diff --git a/sqlc.yaml b/sqlc.yaml deleted file mode 100644 index fa13496..0000000 --- a/sqlc.yaml +++ /dev/null @@ -1,31 +0,0 @@ -version: "2" -cloud: - organization: "" - project: "" - hostname: "" -servers: [] -sql: - - engine: 'postgresql' - schema: 'migrations/*.sql' - queries: 'internal/db/users/queries.sql' - gen: - go: - package: "usersdb" - out: "internal/db/users" - sql_package: "pgx/v5" - emit_json_tags: false - emit_interface: true - overrides: - - db_type: "uuid" - go_type: - import: "github.com/google/uuid" - type: "UUID" - - db_type: "timestamptz" - go_type: - import: "time" - type: "Time" -overrides: - go: null -plugins: [] -rules: [] -options: {} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..a25e9f0 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,17 @@ +use std::env; + +use dotenvy::dotenv; + +#[derive(Debug)] +pub struct Config { + pub db_url: String, +} + +impl Config { + pub fn load() -> Self { + dotenv().ok(); + Self { + db_url: env::var("DB_URL").expect("DB_URL is not configured"), + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..29f1c98 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +mod config; +fn main() { + println!("Hello, world!"); + let cfg = config::Config::load(); + println!("{:?}", cfg) +}