From d009fd4f7b3fef7af1036c5c5ed63834eddc9083 Mon Sep 17 00:00:00 2001 From: Blinov Evgeniy Date: Tue, 20 Feb 2024 18:32:01 +0300 Subject: [PATCH] Fix gorm, swagger. Add infra components --- .env | 26 -------------------------- .env.example | 27 ++++++++++++++++++++------- .gitignore | 4 +++- Makefile | 27 +++++++++++++++++++++++++++ README.md | 31 ++++++++++++++++++++++++++++++- app/container/dic/container.go | 6 +++--- config/base.go | 9 ++++++++- controllers/userController.go | 7 ++++--- docker-compose.yml | 22 ++++++++++++++++++++++ docs/docs.go | 13 ++++++++++--- docs/swagger.json | 13 ++++++++++--- docs/swagger.yaml | 11 ++++++++--- infra/db/postgres_init.sh | 25 +++++++++++++++++++++++++ repositories/userRepository.go | 2 +- routers/api.go | 8 +++++--- 15 files changed, 176 insertions(+), 55 deletions(-) delete mode 100644 .env create mode 100644 Makefile create mode 100644 docker-compose.yml create mode 100755 infra/db/postgres_init.sh diff --git a/.env b/.env deleted file mode 100644 index f2f2b3a..0000000 --- a/.env +++ /dev/null @@ -1,26 +0,0 @@ -BASE_URL=127.0.0.1 -API_PORT=443 - -PROJECT_NAME=gotham -PROJECT_URL=https://www.example.com -PROJECT_API_URL=https://api.example.com - -#DB -DB_CONNECTION=postgres -DB_USERNAME=root -DB_DATABASE=gotham -DB_HOST=localhost -DB_PORT=5432 -DB_PASSWORD=password - -#EMAIL -FROM=example@go-gotham.com -HOST=smtp.gmail.com -PORT=587 -PASSWORD=password - -#VERSION -VERSION=0.1 - -#JWT_SECRET_KEY -JWT_SECRET_KEY=jwt_secret_key diff --git a/.env.example b/.env.example index e34de6a..c11639c 100644 --- a/.env.example +++ b/.env.example @@ -1,20 +1,33 @@ BASE_URL=127.0.0.1 -API_PORT=443 +API_PORT=8000 PROJECT_NAME=gotham +PROJECT_HOST=localhost PROJECT_URL=https://www.example.com PROJECT_API_URL=https://api.example.com #DB -DB_CONNECTION=mysql -DB_DATABASE=example +DB_CONNECTION=postgres +DB_USERNAME=gotham +DB_DATABASE=gotham DB_HOST=localhost -DB_PORT=3306 -DB_USERNAME=admin -DB_PASSWORD=admin +DB_PORT=35432 +DB_PASSWORD=password + +PGUSER=admin +PGPASSWORD=password +PGDATABASE=gotham +PGHOST=localhost +PGPORT=35432 + +#EMAIL +FROM=example@example.com +HOST=smtp.gmail.com +PORT=587 +PASSWORD=password #VERSION VERSION=0.1 #JWT_SECRET_KEY -JWT_SECRET_KEY=7l6dds5z2egrfcw01s6e78arte48067 +JWT_SECRET_KEY=jwt_secret_key diff --git a/.gitignore b/.gitignore index 6b80030..7e84b77 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ # vendor/ -*.idea \ No newline at end of file +*.idea + +.env diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d5da813 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +# vim: set noet ci pi sts=0 sw=4 ts=4 : +# http://www.gnu.org/software/make/manual/make.html +# http://linuxlib.ru/prog/make_379_manual.html +SHELL := $(shell which bash) +#SHELL := $(shell which sh) +DEBUG ?= 0 + +######################################################################## +# Default variables +######################################################################## +-include .env +export +######################################################################## +GOBIN := $(or $(GOBIN), $(GOPATH)/bin) +SUDO := $(or $(SUDO),) +GO111MODULE := $(or $(GO111MODULE), on) +GOROOT := $(GOPATH/src) +TAGS := +LDFLAGS := -w -s +GOFLAGS := + +.PHONY: db-init +db-init: + set -o allexport; + source .env; + set +o allexport; + ./infra/db/$(DB_CONNECTION)_init.sh diff --git a/README.md b/README.md index 2ac3f31..bc13f58 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,21 @@ DB_PORT=3306 DB_PASSWORD=strong_password ``` +## Infra + +``` +cp .env.example .env + +docker-compose up -d +# wait for pg container is up +# login from pgadmin and check database status + +make db-init + +go install github.com/swaggo/swag/cmd/swag@v1.7.0 +swag init -g main.go +``` + ## Flags - prevents re-creating container methods from definitions @@ -115,6 +130,20 @@ go run gotham -seed ### ViewModels ViewModels folder hosts all the structs under viewmodels namespace, viewmodels are model to be use as a response return of REST API call + +## Test + +``` +set -o allexport; +source .env; +set +o allexport; +export ADMIN_EMAIL=$(echo "SELECT json_agg(users) FROM users WHERE id=1" |psql -AXqt | jq -r '.[].email') +export ADMIN_TOKEN=$(curl -s -X POST "http://${BASE_URL}:${API_PORT}/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"email\": \"${ADMIN_EMAIL}\", \"password\": \"password\", \"platform\": \"web\"}" | jq -r .data.access_token) + +curl -X GET "http://${BASE_URL}:${API_PORT}/v1/r/users" -H "accept: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" +curl -X GET "http://${BASE_URL}:${API_PORT}/v1/r/users/2" -H "accept: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" +``` + ## Author @@ -139,4 +168,4 @@ Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/container/dic/container.go b/app/container/dic/container.go index 328d8d7..582fad5 100644 --- a/app/container/dic/container.go +++ b/app/container/dic/container.go @@ -23,9 +23,9 @@ import ( // The function panics if the Container can not be retrieved. // // The interface can be : -// - a *Container -// - an *http.Request containing a *Container in its context.Context -// for the dingo.ContainerKey("dingo") key. +// - a *Container +// - an *http.Request containing a *Container in its context.Context +// for the dingo.ContainerKey("dingo") key. // // The function can be changed to match the needs of your application. var C = func(i interface{}) *Container { diff --git a/config/base.go b/config/base.go index 50ab1e6..8c222e0 100644 --- a/config/base.go +++ b/config/base.go @@ -29,6 +29,7 @@ type Config struct { Brand struct { ProjectName string ProjectUrl string + ProjectHost string ProjectApiUrl string } } @@ -47,7 +48,13 @@ func Configurations() { Brand: struct { ProjectName string ProjectUrl string + ProjectHost string ProjectApiUrl string - }{ProjectName: os.Getenv("PROJECT_NAME"), ProjectUrl: os.Getenv("PROJECT_URL"), ProjectApiUrl: os.Getenv("PROJECT_API_URL")}, + }{ + ProjectName: os.Getenv("PROJECT_NAME"), + ProjectUrl: os.Getenv("PROJECT_URL"), + ProjectHost: os.Getenv("PROJECT_HOST"), + ProjectApiUrl: os.Getenv("PROJECT_API_URL"), + }, } } diff --git a/controllers/userController.go b/controllers/userController.go index a7d95ab..64a704e 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -26,7 +26,7 @@ type UserController struct { // @Accept multipart/form-data // @Accept application/x-www-form-urlencoded // @Produce json -// @Param token header string true "Bearer Token" +// @Param Authorization header string true "Bearer Token" // @Success 200 {object} viewModels.Paginator{data=[]models.User} // @Failure 400 {object} viewModels.Message{} // @Failure 401 {object} viewModels.Message{} @@ -70,14 +70,15 @@ func (u UserController) Index(c echo.Context) (err error) { // @Accept multipart/form-data // @Accept application/x-www-form-urlencoded // @Produce json -// @Param token header string true "Bearer Token" +// @Param user path string true "1" +// @Param Authorization header string true "Bearer Token" // @Success 200 {object} viewModels.HTTPSuccessResponse{data=models.User} // @Failure 404 {object} viewModels.Message{} // @Failure 401 {object} viewModels.Message{} // @Failure 400 {object} viewModels.Message{} // @Failure 403 {object} viewModels.Message{} // @Failure 500 {object} viewModels.Message{} -// @Router /v1/r/users/:user [get] +// @Router /v1/r/users/{user} [get] func (u UserController) Show(c echo.Context) (err error) { auth := models.ConvertUser(c.Get("auth")) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..91a5b0e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + pg: + image: postgres:13.3 + container_name: gotham_pg + restart: always + environment: + POSTGRES_PASSWORD: "password" + POSTGRES_USER: "admin" + ports: + - '35432:5432' + + + pgadmin: + image: dpage/pgadmin4:latest + container_name: gotham_pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: "admin@example.com" + PGADMIN_DEFAULT_PASSWORD: "password" + ports: + - '32082:80' diff --git a/docs/docs.go b/docs/docs.go index 6813a34..7702463 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -164,7 +164,7 @@ var doc = `{ { "type": "string", "description": "Bearer Token", - "name": "token", + "name": "Authorization", "in": "header", "required": true } @@ -212,7 +212,7 @@ var doc = `{ } } }, - "/v1/r/users/:user": { + "/v1/r/users/{user}": { "get": { "consumes": [ "application/json", @@ -227,10 +227,17 @@ var doc = `{ ], "summary": "Get User", "parameters": [ + { + "type": "string", + "description": "1", + "name": "user", + "in": "path", + "required": true + }, { "type": "string", "description": "Bearer Token", - "name": "token", + "name": "Authorization", "in": "header", "required": true } diff --git a/docs/swagger.json b/docs/swagger.json index d0fdd6d..900dcbb 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -144,7 +144,7 @@ { "type": "string", "description": "Bearer Token", - "name": "token", + "name": "Authorization", "in": "header", "required": true } @@ -192,7 +192,7 @@ } } }, - "/v1/r/users/:user": { + "/v1/r/users/{user}": { "get": { "consumes": [ "application/json", @@ -207,10 +207,17 @@ ], "summary": "Get User", "parameters": [ + { + "type": "string", + "description": "1", + "name": "user", + "in": "path", + "required": true + }, { "type": "string", "description": "Bearer Token", - "name": "token", + "name": "Authorization", "in": "header", "required": true } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 5c9dc2c..1a04d87 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -143,7 +143,7 @@ paths: parameters: - description: Bearer Token in: header - name: token + name: Authorization required: true type: string produces: @@ -175,16 +175,21 @@ paths: summary: List of users tags: - User - /v1/r/users/:user: + /v1/r/users/{user}: get: consumes: - application/json - multipart/form-data - application/x-www-form-urlencoded parameters: + - description: "1" + in: path + name: user + required: true + type: string - description: Bearer Token in: header - name: token + name: Authorization required: true type: string produces: diff --git a/infra/db/postgres_init.sh b/infra/db/postgres_init.sh new file mode 100755 index 0000000..46aae37 --- /dev/null +++ b/infra/db/postgres_init.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh +set -e + +echo "[LOG] CREATE DATABASE IF NOT EXISTS $PGDATABASE" +env PGPASSWORD="$PGPASSWORD" PGHOST="$PGHOST" PGPORT="$PGPORT" psql -v ON_ERROR_STOP=1 --username "$PGUSER" --dbname "postgres" <<-EOSQL +SELECT 'CREATE DATABASE $PGDATABASE' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$PGDATABASE')\gexec +EOSQL + +echo "[LOG] CREATE USER $DB_USERNAME IF NOT EXISTS" +env PGPASSWORD="$PGPASSWORD" PGHOST="$PGHOST" PGPORT="$PGPORT" psql -v ON_ERROR_STOP=1 --username "$PGUSER" --dbname "postgres" <<-EOSQL +DO +\$do$ +BEGIN + IF EXISTS ( + SELECT FROM pg_catalog.pg_roles + WHERE rolname = '$DB_USERNAME') THEN + + RAISE NOTICE 'Role "$DB_USERNAME" already exists. Skipping.'; + ELSE + CREATE ROLE $DB_USERNAME LOGIN ENCRYPTED PASSWORD '$DB_PASSWORD'; + END IF; +END +\$do$; +GRANT ALL ON DATABASE $PGDATABASE TO $DB_USERNAME; +EOSQL diff --git a/repositories/userRepository.go b/repositories/userRepository.go index a119a19..74ccc13 100644 --- a/repositories/userRepository.go +++ b/repositories/userRepository.go @@ -69,7 +69,7 @@ func (repository *UserRepository) Migrate() (err error) { } func (repository *UserRepository) GetUsersWithPaginationAndOrder(pagination scopes.GormPager, order scopes.GormOrderer) (users []models.User, totalCount int64, err error) { - err = repository.DB().Scopes(order.ToOrder(models.User{}.TableName(), "id", "id", "created_at", "updated_at")).Count(&totalCount).Scopes(pagination.ToPaginate()).Find(&users).Error + err = repository.DB().Table(models.User{}.TableName()).Scopes(order.ToOrder(models.User{}.TableName(), "id", "id", "created_at", "updated_at")).Count(&totalCount).Scopes(pagination.ToPaginate()).Find(&users).Error return } diff --git a/routers/api.go b/routers/api.go index 7616033..bc6c7a6 100644 --- a/routers/api.go +++ b/routers/api.go @@ -2,6 +2,7 @@ package routers import ( "context" + "fmt" "os" "os/signal" "time" @@ -21,9 +22,9 @@ func Route(e *echo.Echo) { docs.SwaggerInfo.Title = "Gotham API" docs.SwaggerInfo.Description = "..." docs.SwaggerInfo.Version = "1.0" - docs.SwaggerInfo.Host = "''" + docs.SwaggerInfo.Host = fmt.Sprintf("%s:%s", config.Conf.Brand.ProjectHost, config.Conf.Port) docs.SwaggerInfo.BasePath = "/" - docs.SwaggerInfo.Schemes = []string{"v1"} + //docs.SwaggerInfo.Schemes = []string{"v1"} e.Use(middleware.Logger()) e.Use(middleware.Recover()) @@ -40,7 +41,8 @@ func Route(e *echo.Echo) { // login v1.POST("/login", app.Application.Container.GetAuthController().Login) - r := v1.Group("/restricted") + //r := v1.Group("/restricted") + r := v1.Group("/r") c := middleware.JWTConfig{ Claims: &config.JwtCustomClaims{},