Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
484bb82
chore: :rocket: start app
cassiusbessa Apr 3, 2025
8d21c06
feat: :sparkles: ensure create email with valid values
cassiusbessa Apr 3, 2025
3f15a0a
refactor: :recycle: make email imutable
cassiusbessa Apr 3, 2025
3b1ec59
refactor: :recycle: use shared errors
cassiusbessa Apr 3, 2025
ea10fb7
feat: :sparkles: ensure create phone number with valid values
cassiusbessa Apr 3, 2025
1afc4be
feat: :sparkles: ensure that create user with valid values
cassiusbessa Apr 3, 2025
3cb6278
feat: :sparkles: ensure create password with valid values
cassiusbessa Apr 3, 2025
d7e1284
refactor: :recycle: add password to user
cassiusbessa Apr 3, 2025
057c1ac
test: :white_check_mark: add password to user test
cassiusbessa Apr 3, 2025
f378722
refactor: :recycle: add uuid to entity user
cassiusbessa Apr 3, 2025
a84f128
build: :hammer: dockerize app
cassiusbessa Apr 4, 2025
c0aa7b8
feat: :sparkles: create data base model
cassiusbessa Apr 4, 2025
44857bc
refactor: :recycle: change table name
cassiusbessa Apr 4, 2025
0d1565a
feat: :sparkles: ensure that create user works with valid param and s…
cassiusbessa Apr 4, 2025
ed7891f
feat: :sparkles: create user gorm repository
cassiusbessa Apr 4, 2025
ad9e56a
feat: :sparkles: add json struct mapper
cassiusbessa Apr 4, 2025
bd460a2
refactor: :truck: rename folder
cassiusbessa Apr 4, 2025
89299b1
refactor: :recycle: apply ports pattern
cassiusbessa Apr 4, 2025
de3d516
feat: :sparkles: ensure that handler works with valid input and retur…
cassiusbessa Apr 4, 2025
6c1eb6b
feat: :sparkles: implemente create user route
cassiusbessa Apr 4, 2025
ca96143
fix: :bug: fix error response
cassiusbessa Apr 4, 2025
585b5e6
refactor: :recycle: return nil in record not found error
cassiusbessa Apr 4, 2025
2347436
fix: :bug: improved response in error case
cassiusbessa Apr 4, 2025
7e5b28d
feat: :sparkles: ensure that login works with not found user, diferre…
cassiusbessa Apr 4, 2025
7e562bc
feat: :sparkles: implement jwt token
cassiusbessa Apr 4, 2025
787f252
test: :recycle: refactor tests with mocks
cassiusbessa Apr 4, 2025
cc3b098
test: :recycle: refactor create user handler test
cassiusbessa Apr 4, 2025
258c680
refactor: :art: remove comments
cassiusbessa Apr 4, 2025
d1cc3b7
feat: :sparkles: ensure login return error in invalid body, not found…
cassiusbessa Apr 4, 2025
da12436
fix: :bug: no hash password twice
cassiusbessa Apr 4, 2025
be204e4
feat: :sparkles: create login route
cassiusbessa Apr 4, 2025
9d8789a
feat: :sparkles: ensure that return error when invalid token or not f…
cassiusbessa Apr 4, 2025
1b54808
test: :recycle: improve code style
cassiusbessa Apr 4, 2025
b5faca1
refactor: :recycle: create and use loaded user dto instead user entity
cassiusbessa Apr 4, 2025
6b299ea
feat: :sparkles: ensure that returns error if token is not valid or a…
cassiusbessa Apr 4, 2025
cf6efa2
refactor: :recycle: apply DRY in factories
cassiusbessa Apr 4, 2025
4cb7cf4
feat: :sparkles: add load user by token route
cassiusbessa Apr 4, 2025
c11e93e
fix: :bug: fix generate new user id and load user by id
cassiusbessa Apr 4, 2025
45eb6de
feat: :sparkles: add code invitation and points
cassiusbessa Apr 4, 2025
0478fe1
test: :white_check_mark: ensures that users are created with a starti…
cassiusbessa Apr 4, 2025
1de6d78
feat: :sparkles: add point to inviter user
cassiusbessa Apr 4, 2025
e635519
refactor: :art: improve code style
cassiusbessa Apr 5, 2025
d850f07
feat: :sparkles: implement email sent to inviter user
cassiusbessa Apr 5, 2025
7d36653
feat: :sparkles: ensure that returns users ordered by points
cassiusbessa Apr 5, 2025
b9aaef1
feat: :sparkles: implement get user ranking route
cassiusbessa Apr 5, 2025
774aa55
fix: :bug: check if user is nil
cassiusbessa Apr 5, 2025
4b63930
feat: :sparkles: add cors
cassiusbessa Apr 6, 2025
a0db8c3
refactor: :recycle: phone number more testable
cassiusbessa Apr 6, 2025
df321c8
fix: :bug: handle empty invite code and invite code not valid
cassiusbessa Apr 6, 2025
59fae14
front end base
cassiusbessa Apr 6, 2025
438d7c1
fix get user response
cassiusbessa Apr 6, 2025
9dfef7a
refactor: :sparkles: code link more easy to front
cassiusbessa Apr 6, 2025
49ec57a
handler invitation better
cassiusbessa Apr 6, 2025
042be99
Initial commit
cassiusbessa Apr 6, 2025
fc26b24
Add 'frontend/' from commit '49ec57a0b17d1a34ae063b5e9924d2ee50b4feba'
cassiusbessa Apr 6, 2025
472d2ae
Add 'backend/' from commit '9dfef7aa041ae8abe138764d8f0ae00d202798c7'
cassiusbessa Apr 6, 2025
689fef2
refactor: :fire: remove bolt
cassiusbessa Apr 6, 2025
0aa99c1
build: :construction_worker: dockerize front and backend
cassiusbessa Apr 6, 2025
861f8ba
fix: :bug: add points to inviter use only if success save account
cassiusbessa Apr 6, 2025
8497f28
prepare to deploy
cassiusbessa Apr 6, 2025
ed85441
refactor: :art: use env to base backend url
cassiusbessa Apr 6, 2025
e167d8d
feat: :sparkles: ensure that send email to winners and reset score
cassiusbessa Apr 6, 2025
6e758d0
feat: :sparkles: implement finish competition route
cassiusbessa Apr 6, 2025
ccbf16b
refactor: :fire: remove nginx
cassiusbessa Apr 6, 2025
bae7969
refactor: :fire: remove comments
cassiusbessa Apr 6, 2025
062d1da
docs: :memo: readme and env example
cassiusbessa Apr 6, 2025
7e507aa
docs: :memo: add name in readme
cassiusbessa Apr 7, 2025
89b6749
Merge branch 'main' into pr
cassiusbessa Apr 7, 2025
2935bdc
restore: bring back original fork content after merge
cassiusbessa Apr 7, 2025
5a9f8e8
docs: :memo: fix docs infos
cassiusbessa Apr 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Banco
DB_HOST=db
DB_PORT=5432
DB_USER=user
DB_PASSWORD=password
DB_NAME=vbio
DB_URL=postgresql://user:password@db:5432/vbio
# Backend
APP_PORT=8080
JWT_SECRET=segredo
BREVO_API_KEY=***
REACT_APP_URL=http://localhost:3000
# Frontend
FRONTEND_PORT=3000
NEXT_PUBLIC_API_URL=http://localhost:8080/api
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
151 changes: 116 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,128 @@
# **TESTE BACKEND**
# 🛠️ Bvio Mono

## SITUAÇÂO-PROBLEMA
A ideia é construir uma página de competição muito simples que incentiva as pessoas a divulgarem notícias sobre compensações de carbono. Os usuários chegam à página e preenchem um formulário de inscrição. Após uma inscrição bem-sucedida, eles ganham um ponto para a competição. Neste momento, eles têm a opção de compartilhar o link de inscrição. Cada inscrição bem-sucedida feita através do link compartilhado dará pontos extras para o autor original do link. Não há limite para o número de pontos que uma pessoa pode obter. No final da competição, as 10 pessoas com mais pontos vencem.
Monorepo da aplicação **Bvio**, uma plataforma com foco em competições, rankings e recompensas via sistema de indicações.

---------------------------------------------------------------------
Este repositório contém tanto o **backend** (Go) quanto o **frontend** (Next.js), com orquestração via Docker e arquitetura orientada por **DDD (Domain-Driven Design)** no backend.

## REQUISITOS OBRIGATÓRIOS
- O formulário de inscrição consiste em nome, e-mail e números de telefone;
- Quando o formulário é enviado, o usuário ganha um ponto e será direcionado para uma página com opção de compartilhar o link especial;
- Quando as pessoas chegam à página da competição através do link especial, o autor original ganha um ponto extra;
- Após o término da competição, gerar uma tabela dos vencedores;
- Enviar notificação via e-mail para os ganhadores e para cada vez que alguém fizer um ponto a partir do link filiado.
- Seja original, projetos suspeitos de serem copiados serão descartados
- Queremos ver seu codigo, não o de outros.
- Criar coleção no Postman (seu teste será testado por aqui).
- Criar um frontend que consuma a API(React)
---

---------------------------------------------------------------------
## ✨ Funcionalidades

## GIT
- Faça um fork deste repositório.
- Crie uma branch para codar as suas features.
- Faça um pull-request quando o teste for finalizado.
- Cadastro de usuários com código de convite único
- Autenticação via JWT
- Atribuição de pontos por convites
- Ranking de usuários por pontuação
- Finalização da competição:
- Envio de e-mails para os 10 primeiros colocados
- Reset da pontuação de todos os usuários

##### **NOTA: Será avaliado também se o nome da branch, títulos de commit, push e comentários possuem boa legibilidade.**
---

---------------------------------------------------------------------
## 🧠 Arquitetura e DDD

## FRAMEWORK
- Servidor: Golang(Fiber ou Gin)
- Banco de dados: MongoDB, DynamoDB, MySQL, Postgres...
A arquitetura segue os princípios de Domain-Driven Design:

-------------------------------------------------------
- **Domain Layer**: contém entidades e encapsulamento das regras das mesmas
- **Application Layer**: orquestra os casos de uso
- **Infrastructure Layer**: implementações concretas de repositórios (DB, e-mail)
- **Interface Layer**: entrega da aplicação (ex: HTTP handlers com Gin)

## REQUISITOS DIFERENCIAIS:
- Seguir os princípios de SOLID.
- Codar um código performático.
- Utilizar inglês no projeto todo.
- Utilizar Injeção de dependências.
- Fazer deploy do mesmo (heroku, aws, google cloud ou outro da preferência).
### Benefícios

## ENTREGA
- Alta testabilidade
- Baixo acoplamento
- Clareza na separação de responsabilidades
- Facilita manutenção e extensão de funcionalidades

---

## 🐳 Docker

A aplicação é inteiramente dockerizada. Para subir o ambiente localmente:

```bash
docker-compose up --build
```

Serviços disponíveis:

- `backend`: aplicação Go na porta `${APP_PORT}`
- `frontend`: aplicação Next.js na porta `${FRONTEND_PORT}`
- `db`: banco PostgreSQL na porta `5432`

---

## 🔐 Autenticação

- Feita via **JWT**
- Após login, o token é retornado no corpo da resposta
- Rotas privadas requerem o cabeçalho:

```
Authorization: <token>
```

---

## 🥪 Testes

Os testes unitários são escritos com `testify` e `testify/mock`.

### Estrutura dos testes:

1. Setup de mocks
2. Execução do caso de uso
3. Verificações com `assert` e `AssertExpectations`

```bash
cd backend
go test ./...
```

---

## 🌐 Variáveis de Ambiente

As variáveis estão centralizadas em um único arquivo `.env`:

```env
# Backend
APP_PORT=8080

# Frontend
FRONTEND_PORT=3000
NEXT_PUBLIC_API_URL=http://localhost:8080

# Banco de dados
DB_NAME=bvio
DB_USER=postgres
DB_PASSWORD=secret
```

O Docker Compose injeta esse `.env` automaticamente nos serviços.

---

## 🚀 Deploy

### Backend

- Hospedado na [Render](https://render.com)

---

## 🤝 Contribuindo

Contribuições são bem-vindas! Sinta-se à vontade para abrir:

- Issues com dúvidas, bugs ou sugestões
- Pull Requests com melhorias ou novas funcionalidades

---

## 📬 Contato

Feito com 💙 por Cássius Queiroz Bessa.

Se tiver dúvidas ou quiser trocar ideias, abra uma issue ou entre em contato!

- Faça um pull request e nomeie-o como no ex.: Teste de (Seu nome aqui).
- Envie um email para morelli@gss.eco com o link do pull request, do deploy (tanto do front quanto do back se feito), e anexe a coleção do postman.
- Assim que avaliarmos seu teste, enviaremos uma devolutiva de sucesso ou falha, e caso seja aprovado, um link para agendar sua entrevista técnica.
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
20 changes: 20 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM golang:1.24.1 AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

RUN go build -o backend-test ./cmd/main.go

FROM gcr.io/distroless/base-debian12

WORKDIR /app

COPY --from=builder /app/backend-test .

EXPOSE 8080

CMD ["./backend-test"]
53 changes: 53 additions & 0 deletions backend/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"log"
"os"
"time"

"github.com/cassiusbessa/backend-test/internal/infra/db"
"github.com/cassiusbessa/backend-test/internal/interfaces/http/routes"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
)

func main() {
if err := godotenv.Load(); err != nil {
log.Println("No .env file found")
}

db.Connect()

port := os.Getenv("APP_PORT")
if port == "" {
port = "8080"
}

r := gin.Default()

r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Authorization", "Content-Type"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))

r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})

api := r.Group("/api")
routes.WithCreateUser(api)
routes.WithLogin(api)
routes.WithLoadUserByToken(api)
routes.WithUsersRanking(api)
routes.WithFinishCompetition(api)

log.Printf("Server running on port %s 🚀", port)
if err := r.Run(":" + port); err != nil {
log.Fatal(err)
}
}
60 changes: 60 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module github.com/cassiusbessa/backend-test

go 1.24.1

require (
github.com/gin-contrib/cors v1.7.5
github.com/gin-gonic/gin v1.10.0
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.36.0
gorm.io/driver/postgres v1.5.11
gorm.io/gorm v1.25.12
)

require (
github.com/antihax/optional v1.0.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
)

require (
github.com/bytedance/sonic v1.13.2 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/getbrevo/brevo-go v1.1.3
github.com/gin-contrib/sse v1.0.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.26.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/text v0.2.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.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.15.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading