innovationhub-cli 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -86
- package/index.js +228 -29
- package/package.json +12 -3
- package/templates/nest/.env.example +41 -0
- package/templates/nest/.prettierrc +4 -0
- package/templates/nest/Dockerfile +17 -0
- package/templates/nest/README.md +224 -0
- package/templates/nest/addons/.github/dependabot.yml +11 -0
- package/templates/nest/addons/.github/labeler.yml +34 -0
- package/templates/nest/addons/.github/workflows/check-branch.yml +24 -0
- package/templates/nest/addons/.github/workflows/ci.yml +29 -0
- package/templates/nest/addons/.github/workflows/draft-release.yml +22 -0
- package/templates/nest/addons/.github/workflows/pr-labeler.yml +16 -0
- package/templates/nest/addons/.github/workflows/release.yml +33 -0
- package/templates/nest/addons/.github/workflows/security-audit.yml +17 -0
- package/templates/nest/addons/.github/workflows/semantic-pr.yml +31 -0
- package/templates/nest/addons/.github/workflows/stale.yml +24 -0
- package/templates/nest/addons/.husky/commit-msg +17 -0
- package/templates/nest/addons/.husky/pre-commit +1 -0
- package/templates/nest/addons/addons.json +17 -0
- package/templates/nest/addons/cloudinary/cloudinary.module.ts +11 -0
- package/templates/nest/addons/cloudinary/cloudinary.provider.ts +16 -0
- package/templates/nest/addons/cloudinary/cloudinary.service.ts +54 -0
- package/templates/nest/docker-compose.yml +58 -0
- package/templates/nest/eslint.config.mjs +34 -0
- package/templates/nest/jest.config.js +17 -0
- package/templates/nest/nest-cli.json +12 -0
- package/templates/nest/package.json +99 -0
- package/templates/nest/src/app.controller.ts +7 -0
- package/templates/nest/src/app.module.ts +69 -0
- package/templates/nest/src/app.service.ts +4 -0
- package/templates/nest/src/auth/auth.controller.ts +97 -0
- package/templates/nest/src/auth/auth.module.ts +46 -0
- package/templates/nest/src/auth/auth.service.ts +231 -0
- package/templates/nest/src/auth/decorators/roles.decorator.ts +5 -0
- package/templates/nest/src/auth/dto/change-password.dto.ts +21 -0
- package/templates/nest/src/auth/dto/login-response.dto.ts +25 -0
- package/templates/nest/src/auth/dto/login.dto.ts +15 -0
- package/templates/nest/src/auth/dto/refresh-token.dto.ts +12 -0
- package/templates/nest/src/auth/entities/refresh-token.entity.ts +18 -0
- package/templates/nest/src/auth/enums/role.enum.ts +4 -0
- package/templates/nest/src/auth/guards/jwt-auth.guard.ts +5 -0
- package/templates/nest/src/auth/guards/refresh-token.guard.ts +5 -0
- package/templates/nest/src/auth/guards/roles.guard.ts +23 -0
- package/templates/nest/src/auth/interfaces/jwt-payload.interface.ts +10 -0
- package/templates/nest/src/auth/strategies/jwt.strategy.ts +28 -0
- package/templates/nest/src/auth/strategies/local.strategy.ts +23 -0
- package/templates/nest/src/auth/strategies/refresh-token.strategy.ts +32 -0
- package/templates/nest/src/common/base.entity.ts +19 -0
- package/templates/nest/src/common/base.repository.ts +79 -0
- package/templates/nest/src/common/base.service.ts +28 -0
- package/templates/nest/src/common/constants/errors.constants.ts +33 -0
- package/templates/nest/src/common/decorators/user.decorator.ts +9 -0
- package/templates/nest/src/common/dto/base-query.dto.ts +56 -0
- package/templates/nest/src/common/irepository.ts +18 -0
- package/templates/nest/src/common/utils/duration.utils.ts +33 -0
- package/templates/nest/src/common/utils/pagination.utils.ts +35 -0
- package/templates/nest/src/common/utils/slug.utils.ts +14 -0
- package/templates/nest/src/common/utils/transform.utils.ts +62 -0
- package/templates/nest/src/common/validators/is-date-after.validator.ts +40 -0
- package/templates/nest/src/data-source.ts +23 -0
- package/templates/nest/src/main.ts +44 -0
- package/templates/nest/src/user/dto/create-user.dto.ts +50 -0
- package/templates/nest/src/user/dto/query-users.dto.ts +23 -0
- package/templates/nest/src/user/dto/update-user.dto.ts +15 -0
- package/templates/nest/src/user/entities/user.entity.ts +66 -0
- package/templates/nest/src/user/user.controller.ts +172 -0
- package/templates/nest/src/user/user.module.ts +15 -0
- package/templates/nest/src/user/user.repository.ts +61 -0
- package/templates/nest/src/user/user.service.ts +138 -0
- package/templates/nest/template.json +5 -0
- package/templates/nest/test/jest-e2e.json +12 -0
- package/templates/nest/tsconfig.build.json +4 -0
- package/templates/nest/tsconfig.json +25 -0
- package/templates/python/.env.example +37 -0
- package/templates/python/Dockerfile +18 -0
- package/templates/python/README.md +219 -0
- package/templates/python/addons/.github/dependabot.yml +11 -0
- package/templates/python/addons/.github/labeler.yml +29 -0
- package/templates/python/addons/.github/workflows/check-branch.yml +24 -0
- package/templates/python/addons/.github/workflows/ci.yml +30 -0
- package/templates/python/addons/.github/workflows/draft-release.yml +22 -0
- package/templates/python/addons/.github/workflows/pr-labeler.yml +16 -0
- package/templates/python/addons/.github/workflows/release.yml +30 -0
- package/templates/python/addons/.github/workflows/security-audit.yml +21 -0
- package/templates/python/addons/.github/workflows/semantic-pr.yml +31 -0
- package/templates/python/addons/.github/workflows/stale.yml +24 -0
- package/templates/python/addons/addons.json +17 -0
- package/templates/python/addons/cloudinary/service.py +67 -0
- package/templates/python/addons/pre-commit/.pre-commit-config.yaml +31 -0
- package/templates/python/alembic/env.py +56 -0
- package/templates/python/alembic/script.py.mako +26 -0
- package/templates/python/alembic/versions/.gitkeep +0 -0
- package/templates/python/alembic.ini +39 -0
- package/templates/python/app/__init__.py +0 -0
- package/templates/python/app/auth/__init__.py +5 -0
- package/templates/python/app/auth/dependencies.py +118 -0
- package/templates/python/app/auth/enums.py +6 -0
- package/templates/python/app/auth/models.py +18 -0
- package/templates/python/app/auth/router.py +68 -0
- package/templates/python/app/auth/schemas.py +58 -0
- package/templates/python/app/auth/service.py +180 -0
- package/templates/python/app/common/__init__.py +18 -0
- package/templates/python/app/common/base_model.py +26 -0
- package/templates/python/app/common/base_repository.py +83 -0
- package/templates/python/app/common/errors.py +35 -0
- package/templates/python/app/common/pagination.py +22 -0
- package/templates/python/app/common/schemas.py +20 -0
- package/templates/python/app/common/utils.py +15 -0
- package/templates/python/app/core/__init__.py +4 -0
- package/templates/python/app/core/config.py +55 -0
- package/templates/python/app/core/database.py +20 -0
- package/templates/python/app/main.py +33 -0
- package/templates/python/app/user/__init__.py +4 -0
- package/templates/python/app/user/models.py +26 -0
- package/templates/python/app/user/repository.py +84 -0
- package/templates/python/app/user/router.py +170 -0
- package/templates/python/app/user/schemas.py +60 -0
- package/templates/python/app/user/service.py +114 -0
- package/templates/python/docker-compose.yml +55 -0
- package/templates/python/pyproject.toml +46 -0
- package/templates/python/requirements-dev.txt +7 -0
- package/templates/python/requirements.txt +20 -0
- package/templates/python/template.json +5 -0
- package/utils/template.js +165 -0
- package/utils/git.js +0 -71
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Template FastAPI — Innovation Hub
|
|
2
|
+
|
|
3
|
+
Template base para inicialização de projetos backend com **FastAPI**. Inclui autenticação completa, gerenciamento de usuários e uma estrutura modular pronta para expandir.
|
|
4
|
+
|
|
5
|
+
## ✨ Funcionalidades Incluídas
|
|
6
|
+
|
|
7
|
+
- **Autenticação e Autorização**: Sistema completo de login com JWT (JSON Web Tokens), refresh tokens rotativos e controle de acesso baseado em papéis (Roles).
|
|
8
|
+
- **Gerenciamento de Usuários**: CRUD de usuários com criptografia de senhas (bcrypt), soft delete, paginação e reset de senha por admin.
|
|
9
|
+
- **Base Genérica Reutilizável**: `BaseModel`, `BaseRepository` com generics Python para criar novos módulos rapidamente.
|
|
10
|
+
- **Documentação de API**: Geração automática de documentação interativa com **Swagger (OpenAPI)** — nativa do FastAPI.
|
|
11
|
+
- **Utilitários**: Paginação, geração de slugs, constantes de erro centralizadas e query params base.
|
|
12
|
+
|
|
13
|
+
## 🚀 Tecnologias Utilizadas
|
|
14
|
+
|
|
15
|
+
- **Framework**: [FastAPI](https://fastapi.tiangolo.com/)
|
|
16
|
+
- **Linguagem**: [Python](https://www.python.org/) (3.11+)
|
|
17
|
+
- **Banco de Dados**: [PostgreSQL](https://www.postgresql.org/)
|
|
18
|
+
- **ORM**: [SQLAlchemy 2.0](https://www.sqlalchemy.org/) (async)
|
|
19
|
+
- **Migrations**: [Alembic](https://alembic.sqlalchemy.org/)
|
|
20
|
+
- **Containerização**: [Docker](https://www.docker.com/) e Docker Compose
|
|
21
|
+
- **Autenticação**: [python-jose](https://python-jose.readthedocs.io/) (JWT) + [passlib](https://passlib.readthedocs.io/) (bcrypt)
|
|
22
|
+
- **Validação de Dados**: [Pydantic v2](https://docs.pydantic.dev/)
|
|
23
|
+
- **Configuração**: [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
|
|
24
|
+
- **Testes**: [pytest](https://pytest.org/) + [httpx](https://www.python-httpx.org/)
|
|
25
|
+
- **Lint/Formatação**: [ruff](https://docs.astral.sh/ruff/)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🏁 Como Começar
|
|
30
|
+
|
|
31
|
+
### Pré-requisitos
|
|
32
|
+
|
|
33
|
+
- [Python](https://www.python.org/) (3.11 ou superior)
|
|
34
|
+
- [Docker](https://www.docker.com/get-started) e Docker Compose
|
|
35
|
+
|
|
36
|
+
### 1. Clone o Repositório
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git clone <URL_DO_REPOSITORIO>
|
|
40
|
+
cd <NOME_DO_PROJETO>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Configure as Variáveis de Ambiente
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# No Windows
|
|
47
|
+
copy .env.example .env
|
|
48
|
+
|
|
49
|
+
# No Linux ou macOS
|
|
50
|
+
cp .env.example .env
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Preencha as variáveis no arquivo `.env`. As credenciais padrão do Postgres já funcionam com o Docker Compose.
|
|
54
|
+
|
|
55
|
+
### 3. Instale as Dependências
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Crie um ambiente virtual
|
|
59
|
+
python -m venv .venv
|
|
60
|
+
|
|
61
|
+
# Ative o ambiente virtual
|
|
62
|
+
# Windows:
|
|
63
|
+
.venv\Scripts\activate
|
|
64
|
+
# Linux/macOS:
|
|
65
|
+
source .venv/bin/activate
|
|
66
|
+
|
|
67
|
+
# Instale o projeto
|
|
68
|
+
pip install -e ".[dev]"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 🐳 Executando com Docker (Recomendado)
|
|
74
|
+
|
|
75
|
+
### Subir o Ambiente de Desenvolvimento
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
docker-compose --profile dev up -d --build
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Subir o Ambiente de Produção
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
docker-compose --profile prod up -d --build
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- A API estará disponível em `http://localhost:3000`.
|
|
88
|
+
- O banco de dados PostgreSQL estará acessível na porta `5432`.
|
|
89
|
+
|
|
90
|
+
### Parar o Ambiente
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
docker-compose down
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## ⚙️ Executando Localmente (Sem Docker)
|
|
99
|
+
|
|
100
|
+
### 1. Execute um Banco de Dados PostgreSQL
|
|
101
|
+
|
|
102
|
+
Garanta que uma instância do PostgreSQL esteja rodando e que as credenciais no `.env` estejam corretas.
|
|
103
|
+
|
|
104
|
+
### 2. Execute as Migrations
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
alembic upgrade head
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3. Inicie a Aplicação
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uvicorn app.main:app --reload --port 3000
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A aplicação estará disponível em `http://localhost:3000`.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 📄 Documentação e Endpoints
|
|
121
|
+
|
|
122
|
+
### Acessar a Documentação
|
|
123
|
+
|
|
124
|
+
Com a aplicação em execução:
|
|
125
|
+
|
|
126
|
+
- **Swagger UI**: [http://localhost:3000/api/docs](http://localhost:3000/api/docs)
|
|
127
|
+
- **ReDoc**: [http://localhost:3000/api/redoc](http://localhost:3000/api/redoc)
|
|
128
|
+
|
|
129
|
+
### Resumo dos Endpoints Principais
|
|
130
|
+
|
|
131
|
+
- `POST /auth/login`: Autentica um usuário e retorna tokens JWT.
|
|
132
|
+
- `POST /auth/logout`: Faz logout (revoga refresh token).
|
|
133
|
+
- `POST /auth/refresh`: Atualiza os tokens de acesso usando um refresh token.
|
|
134
|
+
- `PATCH /auth/change-password`: Altera a senha do usuário logado.
|
|
135
|
+
- `GET /users/me`: Retorna o perfil do usuário autenticado.
|
|
136
|
+
- `PATCH /users/me`: Atualiza o perfil do usuário autenticado.
|
|
137
|
+
- `GET /users/`: Lista todos os usuários (admin).
|
|
138
|
+
- `GET /users/paginated`: Lista usuários com paginação e busca (admin).
|
|
139
|
+
- `GET /users/{user_id}`: Busca um usuário pelo ID (admin).
|
|
140
|
+
- `POST /users/`: Cria um novo usuário (admin).
|
|
141
|
+
- `PATCH /users/{user_id}`: Atualiza um usuário pelo ID (admin).
|
|
142
|
+
- `PATCH /users/{user_id}/reset-password`: Reseta a senha de um usuário (admin).
|
|
143
|
+
- `DELETE /users/{user_id}`: Deleta um usuário (admin).
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 📂 Estrutura do Projeto
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
app/
|
|
151
|
+
├── auth/ # Autenticação: JWT, dependencies, schemas, service, router
|
|
152
|
+
├── common/ # Base genérica (model, repository), utilitários, paginação, schemas
|
|
153
|
+
├── core/ # Configuração (settings) e banco de dados (SQLAlchemy async)
|
|
154
|
+
├── user/ # Módulo de gerenciamento de usuários (CRUD completo)
|
|
155
|
+
└── main.py # Ponto de entrada da aplicação (FastAPI bootstrap)
|
|
156
|
+
|
|
157
|
+
alembic/
|
|
158
|
+
├── env.py # Configuração do Alembic
|
|
159
|
+
├── script.py.mako # Template de migration
|
|
160
|
+
└── versions/ # Arquivos de migration gerados
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 🔌 Addons Disponíveis
|
|
166
|
+
|
|
167
|
+
Na pasta `addons/` do repositório de templates, você encontra módulos opcionais:
|
|
168
|
+
|
|
169
|
+
| Addon | Descrição |
|
|
170
|
+
| ------------ | --------------------------------------------------------------------- |
|
|
171
|
+
| `.github` | Workflows de CI/CD, Dependabot, labeler, semantic PR, stale issues |
|
|
172
|
+
| `pre-commit` | `.pre-commit-config.yaml` para Conventional Commits e lint pré-commit |
|
|
173
|
+
| `cloudinary` | Módulo Python para upload, delete e replace de imagens no Cloudinary |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 🔧 Scripts Úteis
|
|
178
|
+
|
|
179
|
+
### Migrations com Alembic
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Gerar uma nova migration
|
|
183
|
+
alembic revision --autogenerate -m "descricao da migration"
|
|
184
|
+
|
|
185
|
+
# Executar migrations pendentes
|
|
186
|
+
alembic upgrade head
|
|
187
|
+
|
|
188
|
+
# Reverter a última migration
|
|
189
|
+
alembic downgrade -1
|
|
190
|
+
|
|
191
|
+
# Ver status das migrations
|
|
192
|
+
alembic current
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Testes
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Executar todos os testes
|
|
199
|
+
pytest
|
|
200
|
+
|
|
201
|
+
# Executar com cobertura
|
|
202
|
+
pytest --cov=app
|
|
203
|
+
|
|
204
|
+
# Executar em modo verboso
|
|
205
|
+
pytest -v
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Lint e Formatação
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Verificar erros com ruff
|
|
212
|
+
ruff check .
|
|
213
|
+
|
|
214
|
+
# Corrigir automaticamente
|
|
215
|
+
ruff check --fix .
|
|
216
|
+
|
|
217
|
+
# Formatar código
|
|
218
|
+
ruff format .
|
|
219
|
+
```
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Area Labels
|
|
2
|
+
area/auth:
|
|
3
|
+
- app/auth/**/*
|
|
4
|
+
|
|
5
|
+
area/user:
|
|
6
|
+
- app/user/**/*
|
|
7
|
+
|
|
8
|
+
area/common:
|
|
9
|
+
- app/common/**/*
|
|
10
|
+
|
|
11
|
+
area/core:
|
|
12
|
+
- app/core/**/*
|
|
13
|
+
|
|
14
|
+
# Type Labels (Inferred from files)
|
|
15
|
+
documentation:
|
|
16
|
+
- README.md
|
|
17
|
+
- LICENSE
|
|
18
|
+
- "**/*.md"
|
|
19
|
+
|
|
20
|
+
ci:
|
|
21
|
+
- .github/**/*
|
|
22
|
+
|
|
23
|
+
test:
|
|
24
|
+
- tests/**/*
|
|
25
|
+
- "**/*test*.py"
|
|
26
|
+
|
|
27
|
+
dependencies:
|
|
28
|
+
- pyproject.toml
|
|
29
|
+
- requirements*.txt
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Check Branch Up-to-Date
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main, develop, master, dev]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
check-up-to-date:
|
|
9
|
+
name: Check if branch is up-to-date
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout code
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
|
|
17
|
+
- name: Check if branch is up-to-date
|
|
18
|
+
run: |
|
|
19
|
+
git fetch origin ${{ github.base_ref }}
|
|
20
|
+
git fetch origin ${{ github.head_ref }}
|
|
21
|
+
if ! git merge-base --is-ancestor origin/${{ github.base_ref }} origin/${{ github.head_ref }}; then
|
|
22
|
+
echo "::error::Branch is behind ${{ github.base_ref }}. Please merge/rebase before continuing."
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main, develop, master, dev]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main, develop, master, dev]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-test:
|
|
10
|
+
name: Build and Test
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11"]
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
- name: Setup Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: pip install -e ".[dev]"
|
|
25
|
+
- name: Lint
|
|
26
|
+
run: ruff check .
|
|
27
|
+
- name: Format check
|
|
28
|
+
run: ruff format --check .
|
|
29
|
+
- name: Test
|
|
30
|
+
run: pytest
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Release Drafter
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
types: [opened, reopened, synchronize]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
update_release_draft:
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: release-drafter/release-drafter@v6
|
|
21
|
+
env:
|
|
22
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: "Pull Request Labeler"
|
|
2
|
+
on:
|
|
3
|
+
pull_request_target:
|
|
4
|
+
types: [opened, synchronize, reopened]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
triage:
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pull-requests: write
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/labeler@v5
|
|
14
|
+
with:
|
|
15
|
+
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
16
|
+
sync-labels: true
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
name: Create Release
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Setup Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.11"
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: pip install -e .
|
|
25
|
+
|
|
26
|
+
- name: Create Release
|
|
27
|
+
uses: softprops/action-gh-release@v1
|
|
28
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
29
|
+
with:
|
|
30
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Security Audit
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop, master, dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop, master, dev]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
audit:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Setup Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.11"
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: pip install -e . pip-audit
|
|
20
|
+
- name: Audit dependencies
|
|
21
|
+
run: pip-audit
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Semantic PR
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types:
|
|
6
|
+
- opened
|
|
7
|
+
- edited
|
|
8
|
+
- synchronize
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
main:
|
|
12
|
+
name: Validate PR title
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: amannn/action-semantic-pull-request@v5
|
|
16
|
+
env:
|
|
17
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
18
|
+
with:
|
|
19
|
+
types: |
|
|
20
|
+
feat
|
|
21
|
+
fix
|
|
22
|
+
docs
|
|
23
|
+
style
|
|
24
|
+
refactor
|
|
25
|
+
perf
|
|
26
|
+
test
|
|
27
|
+
build
|
|
28
|
+
ci
|
|
29
|
+
chore
|
|
30
|
+
revert
|
|
31
|
+
requireScope: false
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: "Stale"
|
|
2
|
+
on:
|
|
3
|
+
schedule:
|
|
4
|
+
- cron: "30 1 */4 * *"
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
stale:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
permissions:
|
|
10
|
+
issues: write
|
|
11
|
+
pull-requests: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/stale@v9
|
|
14
|
+
with:
|
|
15
|
+
stale-issue-message: "Esta issue está inativa há 60 dias. Marque como 'fresco' ou comente para reativar."
|
|
16
|
+
stale-pr-message: "Este PR está inativo há 60 dias. Marque como 'fresco' ou comente para reativar."
|
|
17
|
+
close-issue-message: "Esta issue foi fechada por inatividade."
|
|
18
|
+
close-pr-message: "Este PR foi fechado por inatividade."
|
|
19
|
+
days-before-issue-stale: 60
|
|
20
|
+
days-before-pr-stale: 60
|
|
21
|
+
days-before-issue-close: 7
|
|
22
|
+
days-before-pr-close: 7
|
|
23
|
+
exempt-issue-labels: "security,bug,enhancement,pinned,long-term"
|
|
24
|
+
exempt-pr-labels: "security,bug,enhancement,pinned,long-term"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "cloudinary",
|
|
4
|
+
"label": "Cloudinary (Upload de Imagens)",
|
|
5
|
+
"dest": "app/cloudinary"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": ".github",
|
|
9
|
+
"label": "GitHub Actions (CI/CD)",
|
|
10
|
+
"dest": ".github"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "pre-commit",
|
|
14
|
+
"label": "Pre-commit (Lint & Formatação)",
|
|
15
|
+
"dest": "."
|
|
16
|
+
}
|
|
17
|
+
]
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Addon: Cloudinary — Serviço de upload de imagens.
|
|
3
|
+
|
|
4
|
+
Para usar, adicione 'cloudinary' ao pyproject.toml:
|
|
5
|
+
dependencies = [..., "cloudinary>=1.36.0"]
|
|
6
|
+
|
|
7
|
+
E copie este arquivo para app/cloudinary/service.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import cloudinary
|
|
11
|
+
import cloudinary.uploader
|
|
12
|
+
from fastapi import HTTPException, UploadFile, status
|
|
13
|
+
|
|
14
|
+
from app.common.errors import ERRORS
|
|
15
|
+
from app.core.config import settings
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def configure_cloudinary() -> None:
|
|
19
|
+
"""Configura o Cloudinary. Chame no startup da aplicação."""
|
|
20
|
+
cloudinary.config(
|
|
21
|
+
cloud_name=settings.CLOUDINARY_CLOUD_NAME,
|
|
22
|
+
api_key=settings.CLOUDINARY_API_KEY,
|
|
23
|
+
api_secret=settings.CLOUDINARY_API_SECRET,
|
|
24
|
+
secure=True,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def upload_image(file: UploadFile, folder: str) -> dict:
|
|
29
|
+
"""Faz upload de uma imagem para o Cloudinary."""
|
|
30
|
+
if not file:
|
|
31
|
+
raise HTTPException(
|
|
32
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
33
|
+
detail=ERRORS["IMAGE"]["REQUIRED"],
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
contents = await file.read()
|
|
37
|
+
import base64
|
|
38
|
+
|
|
39
|
+
file_base64 = base64.b64encode(contents).decode("utf-8")
|
|
40
|
+
data_uri = f"data:{file.content_type};base64,{file_base64}"
|
|
41
|
+
|
|
42
|
+
result = cloudinary.uploader.upload(data_uri, folder=folder, resource_type="image")
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def delete_image(public_id: str) -> dict:
|
|
47
|
+
"""Deleta uma imagem do Cloudinary pelo public_id."""
|
|
48
|
+
return cloudinary.uploader.destroy(public_id)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def replace_image(file: UploadFile, folder: str, old_public_id: str | None = None) -> dict:
|
|
52
|
+
"""Substitui uma imagem: deleta a antiga (se existir) e faz upload da nova."""
|
|
53
|
+
if not file:
|
|
54
|
+
raise HTTPException(
|
|
55
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
56
|
+
detail=ERRORS["IMAGE"]["REQUIRED"],
|
|
57
|
+
)
|
|
58
|
+
if old_public_id:
|
|
59
|
+
await delete_image(old_public_id)
|
|
60
|
+
return await upload_image(file, folder)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def delete_images(public_ids: list[str]) -> dict:
|
|
64
|
+
"""Deleta múltiplas imagens do Cloudinary."""
|
|
65
|
+
if not public_ids:
|
|
66
|
+
return {}
|
|
67
|
+
return cloudinary.api.delete_resources(public_ids)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Addon: pre-commit
|
|
2
|
+
# Equivalente ao .husky do NestJS para projetos Python.
|
|
3
|
+
# Para instalar: pip install pre-commit && pre-commit install
|
|
4
|
+
|
|
5
|
+
repos:
|
|
6
|
+
# Ruff: linter e formatter
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.9.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: ruff
|
|
11
|
+
args: [--fix]
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
|
|
14
|
+
# Conventional Commits
|
|
15
|
+
- repo: https://github.com/compilerla/conventional-pre-commit
|
|
16
|
+
rev: v4.0.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: conventional-pre-commit
|
|
19
|
+
stages: [commit-msg]
|
|
20
|
+
args:
|
|
21
|
+
- feat
|
|
22
|
+
- fix
|
|
23
|
+
- docs
|
|
24
|
+
- style
|
|
25
|
+
- refactor
|
|
26
|
+
- perf
|
|
27
|
+
- test
|
|
28
|
+
- build
|
|
29
|
+
- ci
|
|
30
|
+
- chore
|
|
31
|
+
- revert
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from logging.config import fileConfig
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import engine_from_config, pool
|
|
4
|
+
|
|
5
|
+
from alembic import context
|
|
6
|
+
from app.auth.models import RefreshToken # noqa: F401
|
|
7
|
+
from app.common.base_model import Base
|
|
8
|
+
from app.core.config import settings
|
|
9
|
+
|
|
10
|
+
# Importar todos os models para que o Alembic os detecte
|
|
11
|
+
from app.user.models import User # noqa: F401
|
|
12
|
+
|
|
13
|
+
config = context.config
|
|
14
|
+
|
|
15
|
+
# Sobrescrever URL com a do settings
|
|
16
|
+
config.set_main_option("sqlalchemy.url", settings.database_url_sync)
|
|
17
|
+
|
|
18
|
+
if config.config_file_name is not None:
|
|
19
|
+
fileConfig(config.config_file_name)
|
|
20
|
+
|
|
21
|
+
target_metadata = Base.metadata
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def run_migrations_offline() -> None:
|
|
25
|
+
"""Roda migrations em modo 'offline'."""
|
|
26
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
27
|
+
context.configure(
|
|
28
|
+
url=url,
|
|
29
|
+
target_metadata=target_metadata,
|
|
30
|
+
literal_binds=True,
|
|
31
|
+
dialect_opts={"paramstyle": "named"},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
with context.begin_transaction():
|
|
35
|
+
context.run_migrations()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def run_migrations_online() -> None:
|
|
39
|
+
"""Roda migrations em modo 'online'."""
|
|
40
|
+
connectable = engine_from_config(
|
|
41
|
+
config.get_section(config.config_ini_section, {}),
|
|
42
|
+
prefix="sqlalchemy.",
|
|
43
|
+
poolclass=pool.NullPool,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
with connectable.connect() as connection:
|
|
47
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
48
|
+
|
|
49
|
+
with context.begin_transaction():
|
|
50
|
+
context.run_migrations()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if context.is_offline_mode():
|
|
54
|
+
run_migrations_offline()
|
|
55
|
+
else:
|
|
56
|
+
run_migrations_online()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
${imports if imports else ""}
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = ${repr(up_revision)}
|
|
16
|
+
down_revision: Union[str, None] = ${repr(down_revision)}
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
${upgrades if upgrades else "pass"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def downgrade() -> None:
|
|
26
|
+
${downgrades if downgrades else "pass"}
|
|
File without changes
|