[BE-01] feat: Reestruturação da api: 1) Implementação do JWT. 2) Implementação de Configs
This commit is contained in:
parent
a3492cd245
commit
fd4c5aecff
87 changed files with 1371 additions and 756 deletions
|
|
@ -1,26 +0,0 @@
|
|||
# Usa a imagem oficial do Python
|
||||
FROM python:3.12-slim
|
||||
|
||||
# Define diretório de trabalho no container
|
||||
WORKDIR /app
|
||||
|
||||
# Copia o arquivo de dependências
|
||||
COPY requirements.txt .
|
||||
|
||||
# Instala dependências no sistema e no Python
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc libffi-dev libssl-dev python3-dev firebird-dev \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install --no-cache-dir -r requirements.txt \
|
||||
&& apt-get remove -y gcc \
|
||||
&& apt-get autoremove -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copia o restante do projeto para o container
|
||||
COPY . .
|
||||
|
||||
# Expõe a porta padrão do Uvicorn/FastAPI
|
||||
EXPOSE 8000
|
||||
|
||||
# Comando para iniciar o servidor
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
BIN
Api/FBCLIENT.DLL
BIN
Api/FBCLIENT.DLL
Binary file not shown.
164
Api/README.md
164
Api/README.md
|
|
@ -1,26 +1,150 @@
|
|||
# 🧾 MyDocs - Gerador de Documentos
|
||||
# 🧩 Projeto [SAAS]
|
||||
|
||||
Aplicação web para gerenciamento de empresas e geração automatizada de documentos com base em **minutas personalizadas** e **informações cadastradas**.
|
||||
Este projeto é uma **API monolítica modular**, onde cada módulo representa um **domínio específico** da aplicação. Internamente, cada módulo é construído sobre a **Arquitetura Hexagonal**, promovendo separação de responsabilidades, testabilidade e independência da infraestrutura.
|
||||
|
||||
## 🚀 Funcionalidades
|
||||
---
|
||||
|
||||
### ✅ Concluídas:
|
||||
- **Autenticação de Usuário**
|
||||
- Sistema de login seguro
|
||||
- **CRUD de Empresas**
|
||||
- Cadastro, edição, visualização e exclusão de empresas
|
||||
## ⚙️ Visão Geral da Arquitetura
|
||||
|
||||
### 🛠️ Em Desenvolvimento:
|
||||
- **Gerador de Documentos**
|
||||
- Geração dinâmica de documentos com base em minutas e dados de empresas
|
||||
- **Cadastro de Minutas**
|
||||
- Sistema para criação e gerenciamento de templates/minutas de documentos
|
||||
- **Cadastro de Produtos**
|
||||
- Cadastro de produtos vinculados a empresas ou documentos
|
||||
### 🏗️ Estrutura Monolítica Modular
|
||||
|
||||
## 🧩 Tecnologias Utilizadas
|
||||
- Modelo **monolítico**, modularizado por **domínios**
|
||||
- Cada domínio organizado dentro do diretório `packages/`
|
||||
- Componentes por domínio:
|
||||
- **Controllers** (entrada)
|
||||
- **Endpoints** (rotas)
|
||||
- **Schemas** (validação e transformação)
|
||||
- **Actions** (orquestração de lógica)
|
||||
- **Services** (casos de uso)
|
||||
- **Repositories** (acesso a dados)
|
||||
|
||||
- **Backend:** Python / FastApi
|
||||
- **Frontend:** React / NextJs
|
||||
- **Banco de Dados:** MySQL
|
||||
- **Autenticação:** JWT
|
||||
### 🧭 Arquitetura Hexagonal por Módulo
|
||||
|
||||
```text
|
||||
/<domínio>
|
||||
/actions # Orquestra lógica entre services, schemas e repositories
|
||||
/controllers # Interface entre endpoints e actions
|
||||
/endpoints # Define as rotas da API
|
||||
/repositories # Acesso ao Firebird via SQLAlchemy
|
||||
/schemas # Entrada e saída de dados
|
||||
/services # Casos de uso
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Tecnologias Utilizadas
|
||||
|
||||
- **Linguagem:** Python 3.11+
|
||||
- **ORM:** SQLAlchemy
|
||||
- **Banco de Dados:** Firebird
|
||||
- **Driver:** fdb
|
||||
- **Arquitetura:** Hexagonal por módulo
|
||||
- **Organização:** Modular por domínio
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Banco de Dados
|
||||
|
||||
O projeto utiliza **Firebird** como banco principal.
|
||||
|
||||
**Arquivo de configuração:**
|
||||
|
||||
```text
|
||||
Api/config/database/firebird.json
|
||||
```
|
||||
|
||||
**Exemplo:**
|
||||
|
||||
```json
|
||||
{
|
||||
"host": "localhost",
|
||||
"port": 3050,
|
||||
"database": "/caminho/para/database.fdb",
|
||||
"user": "SYSDBA",
|
||||
"password": "masterkey"
|
||||
}
|
||||
```
|
||||
|
||||
**Classe de conexão:**
|
||||
|
||||
```text
|
||||
Api/core/connections/firebird.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧠 SQLAlchemy com Queries Manuais
|
||||
|
||||
Utilizamos SQLAlchemy para:
|
||||
|
||||
- Gerenciar conexões
|
||||
- Preencher parâmetros em queries nativas
|
||||
|
||||
**Exemplo:**
|
||||
|
||||
```python
|
||||
sql = "SELECT * FROM CLIENTES WHERE ID = :id"
|
||||
params = {"id": 123}
|
||||
result = session.execute(text(sql), params).fetchall()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ Estrutura de Diretórios
|
||||
|
||||
```text
|
||||
Api/
|
||||
├── api/
|
||||
│ └── v1/
|
||||
│ ├── packages/
|
||||
│ │ └── administrative/
|
||||
│ │ ├── actions/
|
||||
│ │ ├── controllers/
|
||||
│ │ ├── endpoints/
|
||||
│ │ ├── repositories/
|
||||
│ │ ├── schemas/
|
||||
│ │ └── services/
|
||||
│ └── api.py
|
||||
├── config/
|
||||
│ └── database/firebird.json
|
||||
├── core/
|
||||
│ ├── base/
|
||||
│ ├── connections/
|
||||
│ ├── system/
|
||||
│ ├── utils/
|
||||
│ └── auth.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Executando a API
|
||||
|
||||
```bash
|
||||
# Criar ambiente virtual
|
||||
python -m venv .venv
|
||||
|
||||
# Ativar ambiente virtual
|
||||
source .venv/bin/activate # Linux/macOS
|
||||
.venv\Scripts\activate # Windows
|
||||
|
||||
# Instalar dependências
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Executar a API
|
||||
uvicorn api.v1.api:app --reload
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 Observações
|
||||
|
||||
- Novos domínios devem seguir a estrutura modular.
|
||||
- A arquitetura hexagonal facilita manutenção e futura extração para microsserviços.
|
||||
- A separação entre actions, services e repositories melhora a organização e testabilidade.
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 Autor
|
||||
|
||||
Desenvolvido por **Orius Tecnologia**
|
||||
GitHub / LinkedIn: [seu-link](#)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class BaseAction:
|
||||
|
||||
"""
|
||||
50
Api/abstracts/repository.py
Normal file
50
Api/abstracts/repository.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
from typing import Literal, Optional
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from database.firebird import Firebird
|
||||
|
||||
|
||||
class BaseRepository:
|
||||
def query(self, sql: str, params: Optional[dict] = None):
|
||||
"""Executa uma consulta SQL e retorna o resultado como objeto ResultProxy"""
|
||||
return self._execute(sql, params, fetch="result")
|
||||
|
||||
def fetch_all(self, sql: str, params: Optional[dict] = None):
|
||||
"""Executa uma consulta SQL e retorna todos os registros com mapeamento de colunas"""
|
||||
return self._execute(sql, params, fetch="all")
|
||||
|
||||
def fetch_one(self, sql: str, params: Optional[dict] = None):
|
||||
"""Executa uma consulta SQL e retorna o primeiro registro com mapeamento de colunas"""
|
||||
return self._execute(sql, params, fetch="one")
|
||||
|
||||
def run(self, sql: str, params: Optional[dict] = None):
|
||||
"""Executa um SQL sem retorno de dados (ex: INSERT, UPDATE, DELETE)"""
|
||||
return self._execute(sql, params, fetch="none")
|
||||
|
||||
def run_and_return(self, sql: str, params: Optional[dict] = None):
|
||||
"""Executa SQL com RETURNING e retorna o primeiro registro como dict"""
|
||||
return self._execute(sql, params, fetch="one")
|
||||
|
||||
def _execute(
|
||||
self,
|
||||
sql: str,
|
||||
params: Optional[dict] = None,
|
||||
fetch: Literal["all", "one", "result", "none"] = "result",
|
||||
):
|
||||
engine = Firebird.get_engine()
|
||||
try:
|
||||
with engine.begin() as conn:
|
||||
result = conn.execute(text(sql), params or {})
|
||||
if fetch == "all":
|
||||
return result.mappings().all()
|
||||
elif fetch == "one":
|
||||
return result.mappings().first()
|
||||
elif fetch == "none":
|
||||
return None
|
||||
return result # Result object
|
||||
except SQLAlchemyError as e:
|
||||
print(f"[ERRO SQL]: {e}")
|
||||
raise
|
||||
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import json
|
||||
from types import SimpleNamespace
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
class Config:
|
||||
|
||||
@staticmethod
|
||||
def get():
|
||||
def get(name: str):
|
||||
# Caminho absoluto do arquivo atual
|
||||
base_dir = Path(__file__).resolve().parent
|
||||
|
||||
# Caminho absoluto para o config.json (subindo dois níveis e entrando em config/)
|
||||
config_path = base_dir.parent.parent / 'config' / 'database.json'
|
||||
config_path = base_dir.parent.parent / 'config' / name
|
||||
|
||||
# Carrega o JSON como objeto acessível por ponto
|
||||
with open(config_path, 'r') as f:
|
||||
28
Api/actions/dynamic_import/dynamic_import.py
Normal file
28
Api/actions/dynamic_import/dynamic_import.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import importlib
|
||||
from actions.config.config import Config
|
||||
|
||||
|
||||
class DynamicImport:
|
||||
|
||||
def __init__(self):
|
||||
self.config = Config.get('app.json')
|
||||
self.base = 'packages.v1'
|
||||
|
||||
def set_package(self, name):
|
||||
self.package = name
|
||||
|
||||
def set_table(self, table):
|
||||
self.table = table
|
||||
|
||||
def service(self, name: str, class_name : str):
|
||||
try:
|
||||
# Define o nome do Módulo
|
||||
module_file = f"{name}"
|
||||
# Define o caminho do arquivo
|
||||
path = f"{self.base}.{self.package}.services.{self.table}.{self.config.state}.{module_file}"
|
||||
# Realiza a importação do arquivo
|
||||
module = importlib.import_module(path)
|
||||
clazz = getattr(module, class_name)
|
||||
return clazz
|
||||
except (ImportError, AttributeError) as e:
|
||||
raise ImportError(f"Erro ao importar '{class_name}' de '{path}': {e}")
|
||||
32
Api/actions/file/file.py
Normal file
32
Api/actions/file/file.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
|
||||
class File:
|
||||
|
||||
def create(self, data, caminho_arquivo='storage/temp.json'):
|
||||
try:
|
||||
# Garante que a pasta existe
|
||||
os.makedirs(os.path.dirname(caminho_arquivo), exist_ok=True)
|
||||
|
||||
# Lê dados existentes (ou cria nova lista)
|
||||
if os.path.exists(caminho_arquivo):
|
||||
with open(caminho_arquivo, 'r', encoding='utf-8') as arquivo:
|
||||
try:
|
||||
dados_existentes = json.load(arquivo)
|
||||
if not isinstance(dados_existentes, list):
|
||||
dados_existentes = []
|
||||
except json.JSONDecodeError:
|
||||
dados_existentes = []
|
||||
else:
|
||||
dados_existentes = []
|
||||
|
||||
# Adiciona novo dado
|
||||
dados_existentes.append(data)
|
||||
|
||||
# Salva novamente no arquivo com indentação
|
||||
with open(caminho_arquivo, 'w', encoding='utf-8') as arquivo:
|
||||
json.dump(dados_existentes, arquivo, indent=4, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao salvar o dado: {e}")
|
||||
36
Api/actions/jwt/create_token.py
Normal file
36
Api/actions/jwt/create_token.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
from datetime import datetime, timedelta
|
||||
from jose import jwt
|
||||
from pytz import timezone
|
||||
|
||||
from abstracts.action import BaseAction
|
||||
from actions.config.config import Config
|
||||
|
||||
class CreateToken(BaseAction):
|
||||
def __init__(self):
|
||||
# Busca as configurações da aplicação
|
||||
self.config = Config.get('app.json')
|
||||
|
||||
# Cria o timedelta com base na config
|
||||
self.access_token_expire = timedelta(
|
||||
minutes=self.config.jwt.expire.minute,
|
||||
hours=self.config.jwt.expire.hours,
|
||||
days=self.config.jwt.expire.days
|
||||
)
|
||||
|
||||
def execute(self, tipo_token: str, data : str) -> str:
|
||||
|
||||
sp = timezone('America/Sao_Paulo')
|
||||
agora = datetime.now(tz=sp)
|
||||
expira = agora + self.access_token_expire
|
||||
|
||||
# Define os dados do token
|
||||
payload = {
|
||||
'type' : tipo_token,
|
||||
'exp' : expira,
|
||||
'iat' : agora,
|
||||
'data' : str(data)
|
||||
}
|
||||
|
||||
# Retorna os dados codificados
|
||||
return jwt.encode(payload, self.config.jwt.token, algorithm=self.config.jwt.algorithm)
|
||||
|
||||
24
Api/actions/jwt/get_current_user.py
Normal file
24
Api/actions/jwt/get_current_user.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import Depends, HTTPException, status, Request
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
from actions.jwt.verify_token import VerifyToken # A classe que criamos anteriormente
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # Apenas requerido pelo FastAPI
|
||||
|
||||
def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
|
||||
# Ação que válida o tokne
|
||||
verify_token = VerifyToken()
|
||||
# Obtem o resultado da validação
|
||||
result = verify_token.execute(token)
|
||||
|
||||
# Verifica se a resposta é diferente de inválida
|
||||
if result['status'] != 'valid':
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=result.get('message', 'Token inválido ou expirado'),
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
# Retorna apenas os dados do token
|
||||
return result['payload']
|
||||
57
Api/actions/jwt/verify_token.py
Normal file
57
Api/actions/jwt/verify_token.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
from datetime import datetime
|
||||
from jose import jwt, JWTError, ExpiredSignatureError
|
||||
from pytz import timezone
|
||||
|
||||
from actions.config.config import Config
|
||||
|
||||
|
||||
class VerifyToken:
|
||||
def __init__(self):
|
||||
# Carrega configurações
|
||||
self.config = Config.get('app.json')
|
||||
|
||||
def execute(self, token: str, expected_type: str = 'access-token') -> dict:
|
||||
try:
|
||||
# Decodifica o token
|
||||
payload = jwt.decode(
|
||||
token,
|
||||
self.config.jwt.token,
|
||||
algorithms=[self.config.jwt.algorithm]
|
||||
)
|
||||
|
||||
# Valida expiração
|
||||
exp_timestamp = payload.get("exp")
|
||||
if exp_timestamp is None:
|
||||
raise ValueError("O token não possui data de expiração.")
|
||||
|
||||
# Verifica o tipo de token
|
||||
token_type = payload.get("type")
|
||||
if token_type != expected_type:
|
||||
raise ValueError("Tipo de token inválido.")
|
||||
|
||||
# Verificação opcional: validar campo "data"
|
||||
if "data" not in payload:
|
||||
raise ValueError("Token malformado: campo 'data' ausente.")
|
||||
|
||||
return {
|
||||
"status": "valid",
|
||||
"payload": payload
|
||||
}
|
||||
|
||||
except ExpiredSignatureError:
|
||||
return {
|
||||
"status": "expired",
|
||||
"message": "O token expirou."
|
||||
}
|
||||
|
||||
except JWTError as e:
|
||||
return {
|
||||
"status": "invalid",
|
||||
"message": f"Token inválido: {str(e)}"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Erro na validação do token: {str(e)}"
|
||||
}
|
||||
32
Api/actions/log/log.py
Normal file
32
Api/actions/log/log.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
|
||||
class Log:
|
||||
|
||||
def register(self, data, caminho_arquivo='storage/temp.json'):
|
||||
try:
|
||||
# Garante que a pasta existe
|
||||
os.makedirs(os.path.dirname(caminho_arquivo), exist_ok=True)
|
||||
|
||||
# Lê dados existentes (ou cria nova lista)
|
||||
if os.path.exists(caminho_arquivo):
|
||||
with open(caminho_arquivo, 'r', encoding='utf-8') as arquivo:
|
||||
try:
|
||||
dados_existentes = json.load(arquivo)
|
||||
if not isinstance(dados_existentes, list):
|
||||
dados_existentes = []
|
||||
except json.JSONDecodeError:
|
||||
dados_existentes = []
|
||||
else:
|
||||
dados_existentes = []
|
||||
|
||||
# Adiciona novo dado
|
||||
dados_existentes.append(data)
|
||||
|
||||
# Salva novamente no arquivo com indentação
|
||||
with open(caminho_arquivo, 'w', encoding='utf-8') as arquivo:
|
||||
json.dump(dados_existentes, arquivo, indent=4, ensure_ascii=False)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao salvar o dado: {e}")
|
||||
|
|
@ -1,42 +1,86 @@
|
|||
# handlers.py
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from core.system.exceptions import BusinessRuleException
|
||||
|
||||
from actions.system.exceptions import BusinessRuleException
|
||||
from actions.log.log import Log
|
||||
|
||||
|
||||
def register_exception_handlers(app):
|
||||
|
||||
def __init__ (self):
|
||||
log = Log()
|
||||
|
||||
@app.exception_handler(BusinessRuleException)
|
||||
async def business_rule_exception_handler(request: Request, exc: BusinessRuleException):
|
||||
|
||||
response = {
|
||||
"status": "422",
|
||||
"error": "Regra de negócio",
|
||||
"detail": exc.message
|
||||
}
|
||||
|
||||
# Salva o log em disco
|
||||
Log.register(response, 'storage/temp/business_rule_exception_handler.json')
|
||||
|
||||
return JSONResponse(
|
||||
status_code=422,
|
||||
content={"error": "Regra de negócio", "detail": exc.message}
|
||||
content=response
|
||||
)
|
||||
|
||||
@app.exception_handler(StarletteHTTPException)
|
||||
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
|
||||
response = {
|
||||
"status": exc.status_code,
|
||||
"error": "HTTP Error",
|
||||
"detail": exc.detail
|
||||
}
|
||||
|
||||
# Salva o log em disco
|
||||
Log.register(response, 'storage/temp/http_exception_handler.json')
|
||||
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={"error": "HTTP Error", "detail": exc.detail}
|
||||
content=response
|
||||
)
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
||||
|
||||
response = {
|
||||
"status": 400,
|
||||
"error": "Erro de validação",
|
||||
"detail": exc.errors()
|
||||
}
|
||||
|
||||
# Salva o log em disco
|
||||
Log.register(response, 'storage/temp/validation_exception_handler.json')
|
||||
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={"error": "Erro de validação", "detail": exc.errors()}
|
||||
content=response
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={
|
||||
|
||||
response = {
|
||||
"status": 500,
|
||||
"error": "Erro Interno do Servidor",
|
||||
"type": type(exc).__name__,
|
||||
"message": str(exc),
|
||||
"trace": traceback.format_exc()
|
||||
}
|
||||
|
||||
# Salva o log em disco
|
||||
Log.register(response, 'storage/temp/validation_exception_handler.json')
|
||||
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content=response
|
||||
)
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import re
|
||||
|
||||
|
||||
class CNPJ:
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import re
|
||||
|
||||
|
||||
class Email:
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import re
|
||||
import html
|
||||
import re
|
||||
|
||||
|
||||
class Text:
|
||||
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
from core.base.base_action import BaseAction
|
||||
from api.v1.packages.administrative.repositories.c_caixa_item.index import Index
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSearchSchema
|
||||
|
||||
class IndexAction(BaseAction):
|
||||
|
||||
def execute(self, search : CaixaItemSearchSchema):
|
||||
|
||||
# Instânciamento de repositório
|
||||
index = Index()
|
||||
|
||||
# Retorna todos produtos
|
||||
return index.execute(search)
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# Importação de bibliotecas
|
||||
from core.utils.dynamic_import import DynamicImport
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSearchSchema
|
||||
|
||||
class CCaixaItemController:
|
||||
|
||||
def index(self, search : CaixaItemSearchSchema):
|
||||
|
||||
# Importação da classe desejad
|
||||
indexService = DynamicImport.service("administrative", "c_caixa_item", "index_service", "IndexService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.indexService = indexService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return self.indexService.execute(search)
|
||||
|
||||
def create(self, caixa_item_schema: CaixaItemSchema):
|
||||
|
||||
# Importação da classe desejada
|
||||
createService = DynamicImport.service("administrative", "c_caixa_item", "save_service", "SaveService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.createService = createService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return self.createService.execute(caixa_item_schema)
|
||||
|
||||
def show(self, caixa_item_schema: CaixaItemSchema):
|
||||
# Importação da classe desejad
|
||||
showService = DynamicImport.service("administrative", "c_caixa_item", "show_service", "ShowService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.showService = showService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return self.showService.execute(caixa_item_schema)
|
||||
|
||||
def delete(self, caixa_item_schema: CaixaItemSchema):
|
||||
# Importação da classe desejad
|
||||
deleteService = DynamicImport.service("administrative", "c_caixa_item", "delete_service", "DeleteService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.deleteService = deleteService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return self.deleteService.execute(caixa_item_schema)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from core.base.base_repository import BaseRepository
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
|
||||
class Delete(BaseRepository):
|
||||
|
||||
def execute(self, caixa_item : CaixaItemSchema):
|
||||
|
||||
# Realiza a remoção
|
||||
self.cursor.execute(""" DELETE FROM c_caixa_item cci WHERE cci.caixa_item_id = ?""", (caixa_item.caixa_item_id,))
|
||||
|
||||
# Comita a transação
|
||||
self.commit()
|
||||
|
||||
return True
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
from core.base.base_repository import BaseRepository
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSearchSchema
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
class Index(BaseRepository):
|
||||
|
||||
def execute(self, search: Optional[CaixaItemSearchSchema]):
|
||||
print("### Iniciando método execute ###")
|
||||
|
||||
if search is None:
|
||||
print("⚠️ search é None — retornando lista vazia")
|
||||
return []
|
||||
|
||||
where_clauses = []
|
||||
params = []
|
||||
|
||||
for campo, intervalo in search.__dict__.items():
|
||||
print(f"🔍 Campo: {campo}, Valor: {intervalo}, Tipo: {type(intervalo)}")
|
||||
if isinstance(intervalo, BaseModel):
|
||||
date_start = getattr(intervalo, "date_start", None)
|
||||
date_end = getattr(intervalo, "date_end", None)
|
||||
|
||||
if date_start and date_end:
|
||||
where_clauses.append(f"cci.{campo} BETWEEN ? AND ?")
|
||||
params.extend([date_start, date_end])
|
||||
elif date_start:
|
||||
where_clauses.append(f"cci.{campo} >= ?")
|
||||
params.append(date_start)
|
||||
elif date_end:
|
||||
where_clauses.append(f"cci.{campo} <= ?")
|
||||
params.append(date_end)
|
||||
|
||||
# Montagem da SQL
|
||||
sql = """
|
||||
SELECT FIRST 100 *
|
||||
FROM c_caixa_item cci \
|
||||
"""
|
||||
|
||||
if where_clauses:
|
||||
sql += " WHERE " + " AND ".join(where_clauses)
|
||||
|
||||
sql += " ORDER BY caixa_item_id DESC"
|
||||
|
||||
print("✅ SQL Final:", sql)
|
||||
print("📦 Params:", params)
|
||||
|
||||
# Executa a query
|
||||
self.cursor.execute(sql, params)
|
||||
columns = [col[0] for col in self.cursor.description]
|
||||
results = [
|
||||
{columns[i]: row[i] for i in range(len(columns))}
|
||||
for row in self.cursor.fetchall()
|
||||
]
|
||||
|
||||
self.commit()
|
||||
return results
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
from core.base.base_repository import BaseRepository
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
|
||||
class Show(BaseRepository):
|
||||
|
||||
def execute(self, caixa_item_schema: CaixaItemSchema):
|
||||
|
||||
# Executa a busca pelo ID usando placeholder correto (?)
|
||||
self.cursor.execute(
|
||||
"""SELECT * FROM c_caixa_item cci WHERE cci.caixa_item_id = ?""",
|
||||
(caixa_item_schema.caixa_item_id,) # Importante: precisa ser tupla!
|
||||
)
|
||||
|
||||
row = self.cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
# Transforma em dict associativo
|
||||
columns = [desc[0].lower() for desc in self.cursor.description]
|
||||
return dict(zip(columns, row))
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from api.v1.packages.administrative.actions.c_caixa_item.index_action import IndexAction
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSearchSchema
|
||||
|
||||
class IndexService:
|
||||
|
||||
def execute(self, search : CaixaItemSearchSchema):
|
||||
|
||||
# Instânciamento de ações
|
||||
indexAction = IndexAction()
|
||||
|
||||
# Retorna todos produtos desejados
|
||||
return indexAction.execute(search)
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from core.base.base_repository import BaseRepository
|
||||
|
||||
|
||||
class Get(BaseRepository):
|
||||
|
||||
def execute(self, sequencia_schema : GSequenciaSchema):
|
||||
|
||||
self.cursor.execute(""" SELECT * FROM G_SEQUENCIA gs WHERE gs.TABELA LIKE ? """,
|
||||
(sequencia_schema.tabela,))
|
||||
|
||||
row = self.cursor.fetchone()
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
# Transforma em dict associativo
|
||||
columns = [desc[0].lower() for desc in self.cursor.description]
|
||||
return dict(zip(columns, row))
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from core.base.base_repository import BaseRepository
|
||||
|
||||
|
||||
class Save(BaseRepository):
|
||||
|
||||
def execute(self, sequencia_schema : GSequenciaSchema):
|
||||
|
||||
self.cursor.execute(""" UPDATE G_SEQUENCIA SET SEQUENCIA = ? WHERE TABELA LIKE ? """,
|
||||
(sequencia_schema.sequencia, sequencia_schema.tabela))
|
||||
|
||||
# Comita a transação
|
||||
self.commit()
|
||||
|
||||
# Retorna como verdadeiro se for salvo com sucesso
|
||||
return sequencia_schema
|
||||
24
Api/config/app.json
Normal file
24
Api/config/app.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"state" : "go",
|
||||
"url": "/api/v1",
|
||||
"log": {
|
||||
"request": {
|
||||
"name": "request.json",
|
||||
"path": "storage/temp"
|
||||
}
|
||||
},
|
||||
"StartupCheck": {
|
||||
"database": true,
|
||||
"disk": true
|
||||
},
|
||||
"jwt" : {
|
||||
"token" : "WYe1zwtlDkh39_X3X3qTSICFDxts4VQrMyGLxnEpGUg",
|
||||
"algorithm" : "HS256",
|
||||
"type" : "",
|
||||
"expire" : {
|
||||
"minute" : 60,
|
||||
"hours" : 24,
|
||||
"days" : 7
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"firebird": {
|
||||
"host": "localhost",
|
||||
"name": "C:/Users/keven/OneDrive/Desktop/Orius/CAIAPONIA.FDB",
|
||||
"port": 3050,
|
||||
"user": "SYSDBA",
|
||||
"password": "302b3c",
|
||||
"charset": "UTF8"
|
||||
}
|
||||
}
|
||||
13
Api/config/database/firebird.json
Normal file
13
Api/config/database/firebird.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"host": "localhost",
|
||||
"name": "C:/Users/keven/OneDrive/Desktop/Orius/CAIAPONIA.FDB",
|
||||
"port": 3050,
|
||||
"user": "SYSDBA",
|
||||
"password": "302b3c",
|
||||
"charset": "UTF8",
|
||||
"pool" : {
|
||||
"pre_ping" : "True",
|
||||
"size" : 5,
|
||||
"max_overflow" :10
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
from pytz import timezone
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import jwt
|
||||
|
||||
from core.configs import settings
|
||||
|
||||
# Define o esquema OAuth2 para login
|
||||
oauth2_schema = OAuth2PasswordBearer(
|
||||
tokenUrl=f"{settings.API_V1_STR}/usuarios/login"
|
||||
)
|
||||
|
||||
# Função para criar um token JWT
|
||||
def create_token(tipo_token: str, tempo_vida: timedelta, sub: str) -> str:
|
||||
payload = {}
|
||||
sp = timezone('America/Sao_Paulo')
|
||||
expira = datetime.now(tz=sp) + tempo_vida
|
||||
|
||||
payload["type"] = tipo_token
|
||||
payload["exp"] = expira
|
||||
payload["iat"] = datetime.now(tz=sp)
|
||||
payload["sub"] = str(sub)
|
||||
|
||||
return jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.ALGORITHM)
|
||||
|
||||
|
||||
# Criação do token de acesso (access_token)
|
||||
def create_access_token(sub: str) -> str:
|
||||
return create_token(
|
||||
tipo_token='access_token',
|
||||
tempo_vida=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES),
|
||||
sub=sub
|
||||
)
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from core.database import get_connection
|
||||
|
||||
class BaseRepository(ABC):
|
||||
"""
|
||||
Classe abstrata base para todos os repositórios do sistema.
|
||||
Fornece conexão com o banco de dados e obriga implementação de um método execute().
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Inicializa a conexão e o cursor do banco de dados.
|
||||
Essa conexão deve ser usada pelas subclasses.
|
||||
"""
|
||||
self.conn = get_connection()
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, *args, **kwargs):
|
||||
"""
|
||||
Método abstrato obrigatório a ser implementado pelas subclasses.
|
||||
Deve conter a lógica principal do repositório.
|
||||
"""
|
||||
pass
|
||||
|
||||
def commit(self):
|
||||
"""
|
||||
Realiza o commit da transação.
|
||||
"""
|
||||
self.conn.commit()
|
||||
|
||||
def rollback(self):
|
||||
"""
|
||||
Realiza o rollback da transação.
|
||||
"""
|
||||
self.conn.rollback()
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Fecha cursor e conexão.
|
||||
"""
|
||||
self.cursor.close()
|
||||
self.conn.close()
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from core.connections.firebird_4 import Firebird4
|
||||
import traceback
|
||||
|
||||
class BaseRepositoryAlchemy(ABC):
|
||||
"""
|
||||
Classe base para repositórios Firebird com SQL puro.
|
||||
Suporte a bind params nomeados (ex: :id), simula uso estilo PDO.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.conn = Firebird4().connect()
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
if exc_type:
|
||||
print("⚠️ Erro durante execução do repositório:")
|
||||
print(f"Tipo: {exc_type.__name__}")
|
||||
print(f"Erro: {exc_value}")
|
||||
print("Traceback:")
|
||||
traceback.print_tb(tb)
|
||||
self.rollback()
|
||||
else:
|
||||
self.commit()
|
||||
self.close()
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def commit(self):
|
||||
if self.conn:
|
||||
try:
|
||||
self.conn.commit()
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao fazer commit: {e}")
|
||||
self.rollback()
|
||||
|
||||
def rollback(self):
|
||||
if self.conn:
|
||||
try:
|
||||
self.conn.rollback()
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao fazer rollback: {e}")
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self.cursor and not self.cursor.closed:
|
||||
self.cursor.close()
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao fechar cursor: {e}")
|
||||
try:
|
||||
if self.conn and not self.conn.closed:
|
||||
self.conn.close()
|
||||
except Exception as e:
|
||||
print(f"❌ Erro ao fechar conexão: {e}")
|
||||
|
||||
def fetch_one(self, query: str, params: dict = {}):
|
||||
self.cursor.execute(query, params)
|
||||
row = self.cursor.fetchone()
|
||||
return self._row_to_dict(row) if row else None
|
||||
|
||||
def fetch_all(self, query: str, params: dict = {}):
|
||||
self.cursor.execute(query, params)
|
||||
return [self._row_to_dict(row) for row in self.cursor.fetchall()]
|
||||
|
||||
def execute_query(self, query: str, params: dict = {}):
|
||||
self.cursor.execute(query, params)
|
||||
|
||||
def _row_to_dict(self, row):
|
||||
return {desc[0]: value for desc, value in zip(self.cursor.description, row)}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
from pydantic_settings import BaseSettings
|
||||
|
||||
# Classe de configurações globais da aplicação
|
||||
class Settings(BaseSettings):
|
||||
# Prefixo base das rotas da API
|
||||
API_V1_STR: str = '/api/v1'
|
||||
|
||||
# URL de conexão com o banco MySQL
|
||||
# Formato: mysql://usuario:senha@host:porta/banco
|
||||
DB_URL: str = "mysql://root:root@127.0.0.1:3306/mydocs"
|
||||
|
||||
# Chave secreta para geração dos tokens JWT
|
||||
JWT_SECRET: str = 'WYe1zwtlDkh39_X3X3qTSICFDxts4VQrMyGLxnEpGUg'
|
||||
|
||||
# Algoritmo usado para assinar o token
|
||||
ALGORITHM: str = 'HS256'
|
||||
|
||||
# Tempo de expiração do token JWT (em minutos): 1 semana
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7
|
||||
|
||||
# Configuração interna do Pydantic
|
||||
class Config:
|
||||
case_sensitive = True # Respeita letras maiúsculas/minúsculas nas variáveis de ambiente
|
||||
|
||||
# Instância única das configurações
|
||||
settings: Settings = Settings()
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import fdb
|
||||
from core.utils.config import Config
|
||||
|
||||
def get_connection():
|
||||
"""
|
||||
Constrói e retorna uma conexão com o banco MySQL
|
||||
utilizando os dados da URL definida nas configurações.
|
||||
"""
|
||||
|
||||
# Obtem as configurações de banco de dados
|
||||
database = Config.get()
|
||||
|
||||
# Constrói o DSN no formato 'hostname/port:database_path'
|
||||
# E essa string é passada como o PRIMEIRO ARGUMENTO POSICIONAL
|
||||
connection_dsn = f"{database.firebird.host}/{database.firebird.port}:{database.firebird.name}"
|
||||
|
||||
return fdb.connect(
|
||||
connection_dsn, # Este é o DSN completo que o driver espera
|
||||
user=database.firebird.user,
|
||||
password=database.firebird.password,
|
||||
charset=database.firebird.charset
|
||||
)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from sqlalchemy import create_engine, text
|
||||
from core.utils.config import Config
|
||||
|
||||
class Firebird4:
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Constrói e retorna uma conexão com o banco Firebird
|
||||
utilizando os dados da URL definida nas configurações.
|
||||
"""
|
||||
|
||||
# Obtem as configurações de banco de dados
|
||||
database = Config.get()
|
||||
|
||||
# Caminho da conexão com o banco de dados
|
||||
dsn = (
|
||||
f"firebird://{database.firebird.user}:"
|
||||
f"{database.firebird.password}@"
|
||||
f"{database.firebird.host}:"
|
||||
f"{database.firebird.port}/"
|
||||
f"{database.firebird.name}"
|
||||
)
|
||||
|
||||
# Retorna a conexão com o banco
|
||||
return create_engine(
|
||||
dsn,
|
||||
echo=False, # Não exibe as query nos logs
|
||||
)
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import fdb
|
||||
from core.utils.config import Config
|
||||
|
||||
def get_connection():
|
||||
"""
|
||||
Constrói e retorna uma conexão com o banco MySQL
|
||||
utilizando os dados da URL definida nas configurações.
|
||||
"""
|
||||
|
||||
# Obtem as configurações de banco de dados
|
||||
database = Config.get()
|
||||
|
||||
# Constrói o DSN no formato 'hostname/port:database_path'
|
||||
# E essa string é passada como o PRIMEIRO ARGUMENTO POSICIONAL
|
||||
connection_dsn = f"{database.firebird.host}/{database.firebird.port}:{database.firebird.name}"
|
||||
|
||||
return fdb.connect(
|
||||
connection_dsn, # Este é o DSN completo que o driver espera
|
||||
user=database.firebird.user,
|
||||
password=database.firebird.password,
|
||||
charset=database.firebird.charset
|
||||
)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
# core/deps.py
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import jwt, JWTError
|
||||
from core.configs import settings
|
||||
from api.v1.packages.users.models.users.users_model import UserModel # <--- Importe o UserModel
|
||||
|
||||
# Define o esquema de segurança OAuth2 (token tipo Bearer)
|
||||
oauth2_schema = OAuth2PasswordBearer(
|
||||
tokenUrl=f"{settings.API_V1_STR}/usuarios/login"
|
||||
)
|
||||
|
||||
# Função que retorna o usuário autenticado com base no token JWT
|
||||
def get_current_user(token: str = Depends(oauth2_schema)) -> dict:
|
||||
credential_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail='Could not validate credentials',
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token,
|
||||
settings.JWT_SECRET,
|
||||
algorithms=[settings.ALGORITHM],
|
||||
options={"verify_aud": False}
|
||||
)
|
||||
|
||||
user_id: str = payload.get("sub")
|
||||
|
||||
if user_id is None:
|
||||
raise credential_exception
|
||||
|
||||
except JWTError:
|
||||
raise credential_exception
|
||||
|
||||
# --- NOVO: Buscar os dados completos do usuário do banco de dados ---
|
||||
# Convert user_id para int, se ele for um string no JWT e int no banco
|
||||
try:
|
||||
user_id_int = int(user_id)
|
||||
except ValueError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Invalid user ID format in token."
|
||||
)
|
||||
|
||||
# Use o UserModel para buscar os dados completos
|
||||
# Adicione um try-except para a chamada ao modelo para capturar erros de DB
|
||||
try:
|
||||
user = UserModel.get_by_id(user_id_int)
|
||||
except Exception as e: # Captura qualquer erro ao buscar no DB
|
||||
print(f"Error fetching user in get_current_user: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to retrieve user data. {str(e)}"
|
||||
)
|
||||
|
||||
if not user:
|
||||
# Se o usuário não for encontrado no DB (mas o token era válido para um ID),
|
||||
# pode indicar um usuário deletado ou um ID inválido no token.
|
||||
raise credential_exception # Ou HTTPException(404, "User associated with token not found")
|
||||
|
||||
return user # Retorna o dicionário completo do usuário
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# core/security.py
|
||||
|
||||
from passlib.context import CryptContext # Contexto de criptografia de senhas
|
||||
from passlib.exc import UnknownHashError # Exceção para hashes não reconhecidos
|
||||
|
||||
# Define contexto de criptografia com esquema bcrypt
|
||||
CRYPTO = CryptContext(schemes=['bcrypt'], deprecated='auto')
|
||||
|
||||
def verify_senha_api(plain_senha_api: str, hashed_senha_api: str) -> bool:
|
||||
"""
|
||||
Compara a senha fornecida em texto puro com o hash armazenado.
|
||||
Retorna False em caso de erro ou formato inválido.
|
||||
"""
|
||||
try:
|
||||
if not plain_senha_api or not hashed_senha_api:
|
||||
return False # Garante que nenhum dos valores seja nulo ou vazio
|
||||
|
||||
# Verifica se a senha corresponde ao hash
|
||||
return CRYPTO.verify(plain_senha_api, hashed_senha_api)
|
||||
|
||||
except UnknownHashError:
|
||||
return False # Hash inválido ou não reconhecido pelo passlib
|
||||
|
||||
except Exception:
|
||||
return False # Falha genérica na verificação
|
||||
|
||||
def hash_senha_api(plain_senha_api: str) -> str:
|
||||
"""
|
||||
Gera o hash da senha em texto puro.
|
||||
"""
|
||||
return CRYPTO.hash(plain_senha_api) # Retorna a senha criptografada com bcrypt
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import importlib
|
||||
|
||||
class DynamicImport:
|
||||
|
||||
state = "go"
|
||||
base = "api.v1.packages"
|
||||
|
||||
@staticmethod
|
||||
def service(package: str, table: str, name: str, class_name : str):
|
||||
try:
|
||||
module_file = f"{name}"
|
||||
path = f"{DynamicImport.base}.{package}.services.{table}.{DynamicImport.state}.{module_file}"
|
||||
module = importlib.import_module(path)
|
||||
clazz = getattr(module, class_name)
|
||||
return clazz
|
||||
except (ImportError, AttributeError) as e:
|
||||
raise ImportError(f"Erro ao importar '{class_name}' de '{path}': {e}")
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
# utils/validation.py
|
||||
|
||||
import re
|
||||
import html
|
||||
|
||||
class InputSanitizer:
|
||||
|
||||
@staticmethod
|
||||
def clean_text(text: str) -> str:
|
||||
"""Trim spaces, escape HTML entities, collapse multiple spaces."""
|
||||
text = text.strip()
|
||||
text = html.escape(text)
|
||||
text = re.sub(r"\s+", " ", text)
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def is_valid_email(email: str) -> bool:
|
||||
"""Check if email has a valid structure"""
|
||||
return bool(re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email))
|
||||
|
||||
@staticmethod
|
||||
def has_script(text: str) -> bool:
|
||||
"""Detect basic XSS attempts"""
|
||||
return "<script" in text.lower() or "javascript:" in text.lower()
|
||||
|
||||
@staticmethod
|
||||
def is_safe(text: str) -> bool:
|
||||
"""Detect common XSS/SQL injection characters or patterns"""
|
||||
blacklist = ["<script", "javascript:", "--", ";", "/*", "*/", "@@", "char(", "nchar(", "varchar(", "alter", "drop", "exec"]
|
||||
text_lower = text.lower()
|
||||
return not any(p in text_lower for p in blacklist)
|
||||
55
Api/database/firebird.py
Normal file
55
Api/database/firebird.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# core/database/connection_manager.py
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from actions.config.config import Config
|
||||
|
||||
|
||||
class Firebird:
|
||||
_engine: Optional[Engine] = None
|
||||
|
||||
@classmethod
|
||||
def get_engine(cls) -> Engine:
|
||||
|
||||
# Obtem as configurações de banco de dados
|
||||
database = Config.get('database/firebird.json')
|
||||
|
||||
# Se nao existir engine, cria uma nova
|
||||
if cls._engine is None:
|
||||
|
||||
# Caminho da conexão com o banco de dados
|
||||
dsn = (
|
||||
f"firebird://{database.user}:"
|
||||
f"{database.password}@"
|
||||
f"{database.host}:"
|
||||
f"{database.port}/"
|
||||
f"{database.name}"
|
||||
)
|
||||
|
||||
# Criação da Engine
|
||||
cls._engine = create_engine(
|
||||
dsn,
|
||||
connect_args={
|
||||
"charset": database.charset,
|
||||
},
|
||||
pool_pre_ping=database.pool.pre_ping,
|
||||
pool_size=database.pool.size,
|
||||
max_overflow=database.pool.max_overflow
|
||||
)
|
||||
|
||||
# Retorna a criação da engine
|
||||
return cls._engine
|
||||
|
||||
@classmethod
|
||||
def dispose(cls):
|
||||
|
||||
# Verifica se existe engine
|
||||
if cls._engine:
|
||||
|
||||
# Se existir encerra a conexão
|
||||
cls._engine.dispose()
|
||||
|
||||
# Anula a engine
|
||||
cls._engine = None
|
||||
62
Api/main.py
62
Api/main.py
|
|
@ -1,28 +1,32 @@
|
|||
# main.py
|
||||
|
||||
# Ajuste para garantir que o diretório base do projeto seja incluído no PYTHONPATH
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Adiciona o diretório atual (onde está o main.py) ao sys.path
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
# Importa a classe principal do FastAPI
|
||||
from fastapi import FastAPI
|
||||
from core.system.handlers import register_exception_handlers
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from pathlib import Path
|
||||
# Importa o middleware de CORS
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
# Importa as configurações globais da aplicação
|
||||
from core.configs import settings
|
||||
from fastapi.responses import Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
# Importa o roteador principal da API versão 1
|
||||
from api.v1.api import api_router
|
||||
from packages.v1.api import api_router
|
||||
from packages.v1.system.service.startup_check_service import \
|
||||
StartupCheckService
|
||||
# Importa as configurações globais da aplicação
|
||||
from core.configs import settings
|
||||
from actions.log.log import Log
|
||||
from actions.config.config import Config
|
||||
from actions.system.handlers import register_exception_handlers
|
||||
|
||||
# Instancia o app FastAPI com um título personalizado
|
||||
app = FastAPI(title='API Mydocs')
|
||||
app = FastAPI(title='SAAS Orius')
|
||||
|
||||
# Controle de erros personalizados
|
||||
register_exception_handlers(app)
|
||||
|
||||
# Adiciona o middleware de CORS
|
||||
|
|
@ -34,6 +38,40 @@ app.add_middleware(
|
|||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.on_event("startup")
|
||||
async def on_startup():
|
||||
|
||||
# Realiza as verificações do servidor
|
||||
startupCheckService = StartupCheckService()
|
||||
|
||||
# Exibe o amarzenamento do servidor
|
||||
print(startupCheckService.execute())
|
||||
|
||||
@app.middleware("http")
|
||||
async def log_tempo_requisicao(request: Request, call_next):
|
||||
|
||||
# Ação responsavel por registrar o log de requisição
|
||||
log = Log()
|
||||
config = Config.get('app.json')
|
||||
|
||||
# Obtem os dados da requisição
|
||||
log_data = {
|
||||
"method": request.method,
|
||||
"url": str(request.url),
|
||||
"headers": dict(request.headers)
|
||||
}
|
||||
|
||||
# Gera o nome do arquivo
|
||||
file = Path(config.log.request.path) / config.log.request.name
|
||||
|
||||
# Registra as requisições
|
||||
log.register(log_data, file)
|
||||
|
||||
# Passa adiante
|
||||
response = await call_next(request)
|
||||
|
||||
return response
|
||||
|
||||
# Inclui as rotas da versão 1 da API com prefixo definido em settings (ex: /api/v1)
|
||||
app.include_router(api_router, prefix=settings.API_V1_STR)
|
||||
|
||||
|
|
|
|||
0
Api/packages/__init__.py
Normal file
0
Api/packages/__init__.py
Normal file
|
|
@ -1,6 +1,9 @@
|
|||
from core.base.base_action import BaseAction
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.administrative.repositories.c_caixa_item.delete import Delete
|
||||
from packages.v1.administrativo.repositories.c_caixa_item.delete import \
|
||||
Delete
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class DeleteAction(BaseAction):
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
from packages.v1.administrativo.repositories.c_caixa_item.index import \
|
||||
Index
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSearchSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class IndexAction(BaseAction):
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Instânciamento de repositório
|
||||
index = Index()
|
||||
|
||||
# Retorna todos produtos
|
||||
return index.execute()
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.administrative.repositories.c_caixa_item.save import Save
|
||||
from core.base.base_action import BaseAction
|
||||
from packages.v1.administrativo.repositories.c_caixa_item.save import Save
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class SaveAction(BaseAction):
|
||||
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.administrative.repositories.c_caixa_item.show import Show
|
||||
from core.base.base_action import BaseAction
|
||||
from packages.v1.administrativo.repositories.c_caixa_item.show import Show
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class ShowAction(BaseAction):
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from abstracts.action import BaseAction
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioLoginSchema
|
||||
from packages.v1.administrativo.repositories.g_usuario.get_by_login_repository import GetByLoginRepository
|
||||
|
||||
|
||||
class GetByLoginAction(BaseAction):
|
||||
|
||||
def execute(self, g_usuario_login_schema : GUsuarioLoginSchema):
|
||||
|
||||
# Instânciamento do repositório de busca pelo login
|
||||
get_by_login_repository = GetByLoginRepository()
|
||||
|
||||
# Execução do repositório
|
||||
return get_by_login_repository.execute(g_usuario_login_schema)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioSchema
|
||||
from packages.v1.administrativo.repositories.g_usuario.get_by_usuario_id_repository import GetByUsuarioIdRepository
|
||||
|
||||
class GetByUsuarioIdAction:
|
||||
|
||||
def execute(self, g_usuario_schema = GUsuarioSchema):
|
||||
|
||||
# Importação do repositório
|
||||
get_by_usuario_id_repository = GetByUsuarioIdRepository()
|
||||
|
||||
# Execução do repositório
|
||||
return get_by_usuario_id_repository.execute(g_usuario_schema)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from abstracts.action import BaseAction
|
||||
from packages.v1.administrativo.repositories.g_usuario.index_repository import IndexRepository
|
||||
|
||||
class IndexAction(BaseAction):
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Instânciamento do repositório sql
|
||||
index_repository = IndexRepository()
|
||||
|
||||
# Execução do sql
|
||||
response = index_repository.execute()
|
||||
|
||||
# Retorno da informação
|
||||
return response
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Importação de bibliotecas
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import (
|
||||
CaixaItemSchema, CaixaItemSearchSchema)
|
||||
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||
|
||||
|
||||
class CCaixaItemController:
|
||||
|
||||
def __init__(self):
|
||||
# Classe responsável por carregar as services de acordo com o estado
|
||||
self.dynamic_import = DynamicImport()
|
||||
# Define o pacote que deve ser carregado
|
||||
self.dynamic_import.set_package("administrativo")
|
||||
# Define a tabela que o pacote pertence
|
||||
self.dynamic_import.set_table("c_caixa_item")
|
||||
|
||||
def index(self):
|
||||
|
||||
# Importação da classe desejad
|
||||
indexService = self.dynamic_import.service("index_service", "IndexService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.indexService = indexService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return {
|
||||
'message' : 'Registros localizados com sucesso',
|
||||
'data': self.indexService.execute()
|
||||
}
|
||||
|
||||
def create(self, caixa_item_schema: CaixaItemSchema):
|
||||
|
||||
# Importação da classe desejada
|
||||
createService = self.dynamic_import.service("save_service", "SaveService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.createService = createService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return {
|
||||
'message' : 'Registros cadastrado com sucesso',
|
||||
'data': self.createService.execute(caixa_item_schema)
|
||||
}
|
||||
|
||||
def show(self, caixa_item_schema: CaixaItemSchema):
|
||||
# Importação da classe desejad
|
||||
showService = self.dynamic_import.service("show_service", "ShowService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.showService = showService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return {
|
||||
'message' : 'Registro localizado com sucesso',
|
||||
'data': self.showService.execute(caixa_item_schema)
|
||||
}
|
||||
|
||||
def delete(self, caixa_item_schema: CaixaItemSchema):
|
||||
# Importação da classe desejad
|
||||
deleteService = self.dynamic_import.service("delete_service", "DeleteService")
|
||||
|
||||
# Intânciamento da classe service
|
||||
self.deleteService = deleteService()
|
||||
|
||||
# Lista todos os produtos
|
||||
return {
|
||||
'message' : 'Registros removido com sucesso',
|
||||
'data': self.deleteService.execute(caixa_item_schema)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import (
|
||||
GUsuarioSchema,
|
||||
GUsuarioLoginSchema
|
||||
)
|
||||
|
||||
class GUsuarioController:
|
||||
|
||||
def __init__(self):
|
||||
# Action responsável por carregar as services de acodo com o estado
|
||||
self.dynamic_import = DynamicImport()
|
||||
# Define o pacote que deve ser carregado
|
||||
self.dynamic_import.set_package("administrativo")
|
||||
# Define a tabela que o pacote pertence
|
||||
self.dynamic_import.set_table("g_usuario")
|
||||
pass
|
||||
|
||||
def login(self, g_usuario_login_schema : GUsuarioLoginSchema):
|
||||
|
||||
# Importação de service de login
|
||||
login_service = self.dynamic_import.service("login_service", "LoginService")
|
||||
|
||||
# Instânciamento da service
|
||||
self.login_service = login_service()
|
||||
|
||||
# Retorna o usuário logado
|
||||
return {
|
||||
'message' : 'Usuário localizado com sucesso',
|
||||
'data' : {
|
||||
'token' : self.login_service.execute(g_usuario_login_schema)
|
||||
}
|
||||
}
|
||||
|
||||
def me(self, current_user):
|
||||
|
||||
# Importação de service de login
|
||||
me_service = self.dynamic_import.service("me_service", "MeService")
|
||||
|
||||
# Instânciamento da service
|
||||
self.me_service = me_service()
|
||||
|
||||
# Retorna o usuário logado
|
||||
return {
|
||||
'message' : 'Usuário localizado com sucesso',
|
||||
'data' : self.me_service.execute(current_user)
|
||||
}
|
||||
|
||||
def index(self):
|
||||
|
||||
# Importação da classe desejada
|
||||
indexService = self.dynamic_import.service("index_service", "IndexService")
|
||||
|
||||
# Instânciamento da classe service
|
||||
self.indexService = indexService()
|
||||
|
||||
# Lista todos os usuários
|
||||
return {
|
||||
'message': 'Usuários localizados com sucesso',
|
||||
'data': self.indexService.execute()
|
||||
}
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
# Importação de bibliotecas
|
||||
from fastapi import APIRouter, status, Depends, Body
|
||||
from typing import Optional
|
||||
from api.v1.packages.administrative.controllers.c_caixa_item_controller import CCaixaItemController
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSearchSchema
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, status
|
||||
|
||||
from actions.jwt.get_current_user import get_current_user
|
||||
|
||||
from packages.v1.administrativo.controllers.c_caixa_item_controller import \
|
||||
CCaixaItemController
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import (
|
||||
CaixaItemSchema, CaixaItemSearchSchema)
|
||||
|
||||
# Inicializar o roteaodr para as rotas de produtos
|
||||
router = APIRouter()
|
||||
|
|
@ -15,31 +20,30 @@ cCaixaItemController = CCaixaItemController()
|
|||
status_code=status.HTTP_200_OK,
|
||||
summary="Busca itens com filtros opcionais",
|
||||
response_description="Lista de itens encontrados com base nos critérios de busca.")
|
||||
async def index(search : Optional[CaixaItemSchema] = Body(default=None)):
|
||||
async def index(current_user : dict = Depends(get_current_user)):
|
||||
# Busca todos os produtos cadastrados
|
||||
response = cCaixaItemController.index(search)
|
||||
response = cCaixaItemController.index()
|
||||
|
||||
# Retornar os dados localizados
|
||||
return {
|
||||
"data": response
|
||||
}
|
||||
return response
|
||||
|
||||
@router.post('/',
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="Cadastrar uma nova receita ou despesa no sistema",
|
||||
response_description="Confirmação do cadastro da receita ou despesa, incluindo detalhes do item criado.")
|
||||
async def save(caixa_item_schema: CaixaItemSchema):
|
||||
async def save(caixa_item_schema: CaixaItemSchema, current_user : dict = Depends(get_current_user)):
|
||||
|
||||
# Salva o produto desejado
|
||||
response = cCaixaItemController.create(caixa_item_schema)
|
||||
|
||||
# Retorna a informação desejada
|
||||
return {
|
||||
"data": response
|
||||
}
|
||||
return response
|
||||
|
||||
@router.get('/{caixa_item_id}', status_code=status.HTTP_200_OK)
|
||||
async def show(caixa_item_id : int):
|
||||
@router.get('/{caixa_item_id}',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary="Busca um registro em específico",
|
||||
response_description="Busca um registro de acordo com o ID informado")
|
||||
async def show(caixa_item_id : int, current_user : dict = Depends(get_current_user)):
|
||||
|
||||
# Armazena o produto id no Schema
|
||||
CaixaItemSchema.caixa_item_id = caixa_item_id
|
||||
|
|
@ -48,12 +52,13 @@ async def show(caixa_item_id : int):
|
|||
response = cCaixaItemController.show(CaixaItemSchema)
|
||||
|
||||
# Retorna a informação desejada
|
||||
return {
|
||||
"data": response
|
||||
}
|
||||
return response
|
||||
|
||||
@router.delete('/{caixa_item_id}', status_code=status.HTTP_200_OK)
|
||||
async def delete(caixa_item_id : int):
|
||||
@router.delete('/{caixa_item_id}',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary="Remove um registro em específico",
|
||||
response_description="Remove um registro de acordo com o ID informado")
|
||||
async def delete(caixa_item_id : int, current_user : dict = Depends(get_current_user)):
|
||||
|
||||
# Armazena o produto id no Schema
|
||||
CaixaItemSchema.caixa_item_id = caixa_item_id
|
||||
|
|
@ -62,6 +67,4 @@ async def delete(caixa_item_id : int):
|
|||
response = cCaixaItemController.delete(CaixaItemSchema)
|
||||
|
||||
# Retorna a informação desejada
|
||||
return {
|
||||
"data": response
|
||||
}
|
||||
return response
|
||||
52
Api/packages/v1/administrativo/endpoints/g_usuario.py
Normal file
52
Api/packages/v1/administrativo/endpoints/g_usuario.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Importação de bibliotecas
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, status
|
||||
|
||||
from actions.jwt.get_current_user import get_current_user
|
||||
|
||||
from packages.v1.administrativo.controllers.g_usuario_controller import GUsuarioController
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import (
|
||||
GUsuarioSchema,
|
||||
GUsuarioLoginSchema
|
||||
)
|
||||
|
||||
# Inicializa o roteador para as rotas de usuário
|
||||
router = APIRouter()
|
||||
|
||||
# Instãnciamento do controller desejado
|
||||
g_usuario_controller = GUsuarioController()
|
||||
|
||||
@router.post('/login',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary='Cria o token de acesso do usuário',
|
||||
response_description='Retorna o token de acesso do usuário')
|
||||
async def index(g_usuario_login_schema : GUsuarioLoginSchema):
|
||||
|
||||
# Busca todos os usuários cadastrados
|
||||
response = g_usuario_controller.login(g_usuario_login_schema)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
||||
@router.get('/me')
|
||||
async def me(current_user: dict = Depends(get_current_user)):
|
||||
|
||||
# Busca todos os usuários cadastrados
|
||||
response = g_usuario_controller.me(current_user)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
||||
|
||||
@router.get('/',
|
||||
status_code=status.HTTP_200_OK,
|
||||
summary='Lista todos os usuário cadastrados',
|
||||
response_description='Lista todos os usuário cadastrados')
|
||||
async def index(current_user: dict = Depends(get_current_user)):
|
||||
|
||||
# Busca todos os usuários cadastrados
|
||||
response = g_usuario_controller.index()
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class Delete(BaseRepository):
|
||||
|
||||
def execute(self, caixa_item : CaixaItemSchema):
|
||||
|
||||
# Montagem do sql
|
||||
sql = """ DELETE FROM c_caixa_item cci WHERE cci.caixa_item_id = :caixaItemId """
|
||||
|
||||
# Preenchimento de parâmetros
|
||||
params = {
|
||||
"caixaItemId" : caixa_item.caixa_item_id
|
||||
}
|
||||
|
||||
#Execução do sql
|
||||
response = self.run(sql, params)
|
||||
|
||||
# Retorna o resultado
|
||||
return response
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSearchSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class Index(BaseRepository):
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Montagem do SQL
|
||||
sql = """ SELECT FIRST 10 * FROM c_caixa_item ORDER BY caixa_item_id DESC """
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_all(sql)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
# Importação de bibliotecas
|
||||
from sqlalchemy import create_engine, text
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from core.connections.firebird_4 import Firebird4
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
class Save():
|
||||
|
||||
def __init__(self):
|
||||
|
||||
firebird = Firebird4()
|
||||
|
||||
self.engine = firebird.connect()
|
||||
class Save(BaseRepository):
|
||||
|
||||
def execute(self, caixa_item : CaixaItemSchema):
|
||||
|
||||
|
|
@ -71,9 +66,8 @@ class Save():
|
|||
"registrado" : caixa_item.registrado
|
||||
}
|
||||
|
||||
# Conexão e execução
|
||||
with self.engine.begin() as conn: # garante commit automático
|
||||
conn.execute(text(sql), params)
|
||||
# Execução do sql
|
||||
response = self.run(sql, params)
|
||||
|
||||
# Retorna os dados registrados
|
||||
return caixa_item
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class Show(BaseRepository):
|
||||
|
||||
def execute(self, caixa_item_schema: CaixaItemSchema):
|
||||
|
||||
# Montagem do SQL
|
||||
sql = """ SELECT * FROM c_caixa_item cci WHERE cci.caixa_item_id = :caixaItemId """
|
||||
|
||||
# Preenchimento dos parâmetros
|
||||
params = {
|
||||
"caixaItemId" : caixa_item_schema.caixa_item_id
|
||||
}
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_one(sql, params)
|
||||
|
||||
# Retorna a informação localizada
|
||||
return response
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioLoginSchema
|
||||
|
||||
class GetByLoginRepository(BaseRepository):
|
||||
|
||||
def execute(self, g_usuario_login_schema : GUsuarioLoginSchema):
|
||||
|
||||
# Montagem do sql
|
||||
sql = """ SELECT FIRST 1 * FROM g_usuario gu WHERE gu.LOGIN LIKE :login"""
|
||||
|
||||
# Preenchimento dos parâmetros
|
||||
params = {
|
||||
"login" : g_usuario_login_schema.login
|
||||
}
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_one(sql, params)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioSchema
|
||||
|
||||
class GetByUsuarioIdRepository(BaseRepository):
|
||||
|
||||
def execute(self, g_usuario_schema = GUsuarioSchema):
|
||||
|
||||
# Define a consulta sql
|
||||
sql = """ SELECT * FROM g_usuario gu WHERE gu.usuario_id = :usuarioId """
|
||||
|
||||
# Preenchimento dos parâmetros SQL
|
||||
params = {
|
||||
'usuarioId': g_usuario_schema.usuario_id
|
||||
}
|
||||
|
||||
# Execução da instrução sql
|
||||
return self.fetch_one(sql, params)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from abstracts.repository import BaseRepository
|
||||
|
||||
class IndexRepository(BaseRepository):
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Montagem do sql
|
||||
sql = """ SELECT * FROM g_usuario """
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_all(sql)
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class CaixaItemSchema(BaseModel):
|
||||
especie_pagamento: Optional[str] = None
|
||||
46
Api/packages/v1/administrativo/schemas/g_usuario_schema.py
Normal file
46
Api/packages/v1/administrativo/schemas/g_usuario_schema.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from pydantic import BaseModel, EmailStr, constr
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
class GUsuarioSchema(BaseModel):
|
||||
usuario_id: Optional[int]
|
||||
trocarsenha: Optional[bool] = False
|
||||
login: Optional[str]
|
||||
senha: Optional[str]
|
||||
situacao: Optional[str]
|
||||
nome_completo: str
|
||||
funcao: Optional[str]
|
||||
assina: Optional[bool] = False
|
||||
sigla: Optional[str]
|
||||
usuario_tab: Optional[str]
|
||||
ultimo_login: Optional[datetime]
|
||||
ultimo_login_regs: Optional[datetime]
|
||||
data_expiracao: Optional[datetime]
|
||||
senha_anterior: Optional[str]
|
||||
andamento_padrao: Optional[str]
|
||||
lembrete_pergunta: Optional[str]
|
||||
lembrete_resposta: Optional[str]
|
||||
andamento_padrao2: Optional[str]
|
||||
receber_mensagem_arrolamento: Optional[bool] = False
|
||||
email: Optional[EmailStr]
|
||||
assina_certidao: Optional[bool] = False
|
||||
receber_email_penhora: Optional[bool] = False
|
||||
foto: Optional[str] # base64 ou caminho para imagem
|
||||
nao_receber_chat_todos: Optional[bool] = False
|
||||
pode_alterar_caixa: Optional[bool] = False
|
||||
receber_chat_certidao_online: Optional[bool] = False
|
||||
receber_chat_cancelamento: Optional[bool] = False
|
||||
cpf: Optional[str]
|
||||
somente_leitura: Optional[bool] = False
|
||||
receber_chat_envio_onr: Optional[bool] = False
|
||||
tipo_usuario: Optional[str]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
class GUsuarioLoginSchema(BaseModel):
|
||||
login: Optional[str] = None
|
||||
senha_api: Optional[str] = None
|
||||
|
||||
class GUsuarioMe(BaseModel):
|
||||
usuario_id: int
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
from fastapi import HTTPException, status
|
||||
from api.v1.packages.administrative.actions.c_caixa_item.delete_action import DeleteAction
|
||||
from api.v1.packages.administrative.actions.c_caixa_item.show_action import ShowAction
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
|
||||
from packages.v1.administrativo.actions.c_caixa_item.delete_action import \
|
||||
DeleteAction
|
||||
from packages.v1.administrativo.actions.c_caixa_item.show_action import \
|
||||
ShowAction
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
|
||||
|
||||
class DeleteService:
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
from packages.v1.administrativo.actions.c_caixa_item.index_action import \
|
||||
IndexAction
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSearchSchema
|
||||
|
||||
|
||||
class IndexService:
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Instânciamento de ações
|
||||
indexAction = IndexAction()
|
||||
|
||||
# Retorna todos produtos desejados
|
||||
return indexAction.execute()
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
from api.v1.packages.administrative.actions.c_caixa_item.save_action import SaveAction
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from api.v1.packages.sequencia.services.g_sequencia.generate_service import GenerateService
|
||||
from packages.v1.administrativo.actions.c_caixa_item.save_action import \
|
||||
SaveAction
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from packages.v1.sequencia.services.g_sequencia.generate_service import \
|
||||
GenerateService
|
||||
|
||||
|
||||
class SaveService:
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
from fastapi import HTTPException, status
|
||||
from api.v1.packages.administrative.actions.c_caixa_item.show_action import ShowAction
|
||||
from api.v1.packages.administrative.schemas.c_caixa_item_schema import CaixaItemSchema
|
||||
|
||||
from packages.v1.administrativo.actions.c_caixa_item.show_action import \
|
||||
ShowAction
|
||||
from packages.v1.administrativo.schemas.c_caixa_item_schema import \
|
||||
CaixaItemSchema
|
||||
|
||||
|
||||
class ShowService:
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
from fastapi import HTTPException, status
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioSchema
|
||||
from packages.v1.administrativo.actions.g_usuario.index_action import IndexAction
|
||||
|
||||
class IndexService:
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Instânciamento de acções
|
||||
index_action = IndexAction()
|
||||
|
||||
# Executa a busca de todas as ações
|
||||
data = index_action.execute()
|
||||
|
||||
# Verifica se foi loalizado registros
|
||||
if not data:
|
||||
# Retorna uma exeção
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail='Não foi possível localizar os usuários'
|
||||
)
|
||||
|
||||
# Retorna as informações localizadas
|
||||
return data
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
from fastapi import HTTPException, status
|
||||
from actions.jwt.create_token import CreateToken
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioLoginSchema
|
||||
from packages.v1.administrativo.actions.g_usuario.get_by_login_action import GetByLoginAction
|
||||
|
||||
class LoginService:
|
||||
|
||||
def execute(self, g_usuario_login_schema : GUsuarioLoginSchema):
|
||||
|
||||
# Instânciamento da action de login
|
||||
get_by_login_action = GetByLoginAction()
|
||||
|
||||
# Execução e retorno da action
|
||||
get_by_login_result = get_by_login_action.execute(g_usuario_login_schema)
|
||||
|
||||
# Verifica se o usuário esta ativo
|
||||
if not get_by_login_result.situacao == 'A':
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail='O usuário encontra-se desativado'
|
||||
)
|
||||
|
||||
# Compa as senhas
|
||||
if not get_by_login_result.senha_api == g_usuario_login_schema.senha_api:
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail='A senha informada não é válida'
|
||||
)
|
||||
|
||||
# Gera o token de acesso
|
||||
create_token = CreateToken()
|
||||
|
||||
# Adiciona os dados do usuário ao token
|
||||
jwtUser = {
|
||||
'usuario_id' : int(get_by_login_result.usuario_id),
|
||||
'login' : str(get_by_login_result.login),
|
||||
'nome' : str(get_by_login_result.login)
|
||||
}
|
||||
|
||||
# Retorna o token dos dados do usuário
|
||||
return create_token.execute('access-token', str(jwtUser))
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import ast
|
||||
from packages.v1.administrativo.schemas.g_usuario_schema import GUsuarioMe
|
||||
from packages.v1.administrativo.actions.g_usuario.get_by_usuario_id_action import GetByUsuarioIdAction
|
||||
|
||||
class MeService:
|
||||
|
||||
def execute(self, current_user):
|
||||
|
||||
get_by_usuario_id_action = GetByUsuarioIdAction()
|
||||
|
||||
# Converte a string para dict de forma segura
|
||||
usuario_data = ast.literal_eval(current_user["data"])
|
||||
|
||||
# Define os dados do schema
|
||||
g_usuario_schema = GUsuarioMe(usuario_id=int(usuario_data["usuario_id"]))
|
||||
|
||||
# Executa a ação em questão
|
||||
return get_by_usuario_id_action.execute(g_usuario_schema)
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
from fastapi import APIRouter
|
||||
|
||||
# Importa os módulos de rotas específicos
|
||||
from api.v1.packages.administrative.endpoints import c_caixa_item
|
||||
from packages.v1.administrativo.endpoints import c_caixa_item
|
||||
from packages.v1.administrativo.endpoints import g_usuario
|
||||
|
||||
# Cria uma instância do APIRouter que vai agregar todas as rotas da API
|
||||
api_router = APIRouter()
|
||||
|
|
@ -11,3 +12,8 @@ api_router = APIRouter()
|
|||
api_router.include_router(
|
||||
c_caixa_item.router, prefix="/administrativo/caixa", tags=["Caixa"]
|
||||
)
|
||||
|
||||
# Inclui as rotas de caixa
|
||||
api_router.include_router(
|
||||
g_usuario.router, prefix="/administrativo/usuarios", tags=["Usuário"]
|
||||
)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from api.v1.packages.sequencia.repositories.g_sequencia.get import Get
|
||||
from core.base.base_action import BaseAction
|
||||
from packages.v1.sequencia.repositories.g_sequencia.get import Get
|
||||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class GetAction(BaseAction):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from api.v1.packages.sequencia.repositories.g_sequencia.save import Save
|
||||
from core.base.base_action import BaseAction
|
||||
from packages.v1.sequencia.repositories.g_sequencia.save import Save
|
||||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class SaveAction(BaseAction):
|
||||
21
Api/packages/v1/sequencia/repositories/g_sequencia/get.py
Normal file
21
Api/packages/v1/sequencia/repositories/g_sequencia/get.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class Get(BaseRepository):
|
||||
|
||||
def execute(self, sequencia_schema : GSequenciaSchema):
|
||||
|
||||
# Montagem da consulta sql
|
||||
sql = """ SELECT FIRST 1 * FROM G_SEQUENCIA gs WHERE gs.TABELA LIKE :tabela """
|
||||
|
||||
# Preenchimento dos parâmetros
|
||||
params = {
|
||||
"tabela" : sequencia_schema.tabela
|
||||
}
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_one(sql, params)
|
||||
|
||||
# Transforma em dict associativo
|
||||
return response
|
||||
25
Api/packages/v1/sequencia/repositories/g_sequencia/save.py
Normal file
25
Api/packages/v1/sequencia/repositories/g_sequencia/save.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class Save(BaseRepository):
|
||||
|
||||
def execute(self, sequencia_schema : GSequenciaSchema):
|
||||
|
||||
# Construção do sql
|
||||
sql = """ UPDATE G_SEQUENCIA
|
||||
SET SEQUENCIA = :sequencia
|
||||
WHERE TABELA LIKE :tabela
|
||||
RETURNING TABELA, SEQUENCIA """
|
||||
|
||||
# Preenchimento de parâmetros
|
||||
params = {
|
||||
"sequencia": sequencia_schema.sequencia,
|
||||
"tabela": sequencia_schema.tabela
|
||||
}
|
||||
|
||||
# Execução do sql
|
||||
response = self.run_and_return(sql, params)
|
||||
|
||||
# Retorna como verdadeiro se for salvo com sucesso
|
||||
return response
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class GSequenciaSchema(BaseModel):
|
||||
tabela: Optional[str] = None
|
||||
sequencia: Optional[str] = None
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from api.v1.packages.sequencia.actions.g_sequencia.get_action import GetAction
|
||||
from api.v1.packages.sequencia.actions.g_sequencia.save_action import SaveAction
|
||||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from packages.v1.sequencia.actions.g_sequencia.get_action import GetAction
|
||||
from packages.v1.sequencia.actions.g_sequencia.save_action import \
|
||||
SaveAction
|
||||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
|
||||
|
||||
class GenerateService:
|
||||
|
|
@ -15,7 +16,7 @@ class GenerateService:
|
|||
sequencia_result = getAction.execute(sequencia_schema)
|
||||
|
||||
# Incrementa a sequência atual
|
||||
sequencia_schema.sequencia = sequencia_result['sequencia'] + 1
|
||||
|
||||
sequencia_schema.sequencia = sequencia_result.sequencia + 1
|
||||
|
||||
# Atualiza a sequência atual
|
||||
return saveAction.execute(sequencia_schema)
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from api.v1.packages.sequencia.actions.g_sequencia.save_action import SaveAction
|
||||
from api.v1.packages.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
from packages.v1.sequencia.actions.g_sequencia.save_action import \
|
||||
SaveAction
|
||||
from packages.v1.sequencia.schemas.g_sequencia import GSequenciaSchema
|
||||
|
||||
|
||||
class SaveService:
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from Api.abstracts.repository import BaseRepository
|
||||
|
||||
|
||||
class FirebirdCheckAction(BaseRepository):
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Montagem do SQL
|
||||
sql = """ SELECT 1 FROM RDB$DATABASE """
|
||||
|
||||
# Execução do sql
|
||||
response = self.fetch_one(sql)
|
||||
|
||||
if response:
|
||||
|
||||
# Dados
|
||||
response = {
|
||||
"status" : "Banco de dados acessível"
|
||||
}
|
||||
|
||||
# Retorna os dados localizados
|
||||
return response
|
||||
24
Api/packages/v1/system/actions/disk/get_size_action.py
Normal file
24
Api/packages/v1/system/actions/disk/get_size_action.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import shutil
|
||||
|
||||
from abstracts.action import BaseAction
|
||||
|
||||
|
||||
class GetSizeAction:
|
||||
|
||||
def execute(self):
|
||||
|
||||
# Verificar espaço em disco
|
||||
total, used, free = shutil.disk_usage("/")
|
||||
|
||||
# Converter de bytes para gigabytes
|
||||
total_gb = total / (1024 ** 3)
|
||||
used_gb = used / (1024 ** 3)
|
||||
free_gb = free / (1024 ** 3)
|
||||
|
||||
return {
|
||||
"total" : round(total_gb, 2),
|
||||
"used" : round(used_gb, 2),
|
||||
"free" : round(free_gb, 2)
|
||||
}
|
||||
|
||||
|
||||
12
Api/packages/v1/system/service/startup_check_service.py
Normal file
12
Api/packages/v1/system/service/startup_check_service.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from packages.v1.system.actions.disk.get_size_action import GetSizeAction
|
||||
|
||||
|
||||
class StartupCheckService:
|
||||
|
||||
def execute(self):
|
||||
|
||||
get_size_action = GetSizeAction()
|
||||
get_size_action_result = get_size_action.execute()
|
||||
|
||||
return get_size_action_result
|
||||
|
||||
55
Api/storage/temp.json
Normal file
55
Api/storage/temp.json
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
[
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/http_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json",
|
||||
"storage/temp/validation_exception_handler.json"
|
||||
]
|
||||
0
Api/storage/temp/request.json
Normal file
0
Api/storage/temp/request.json
Normal file
|
|
@ -31,19 +31,13 @@
|
|||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "formdata",
|
||||
"formdata": [
|
||||
{
|
||||
"key": "username",
|
||||
"value": "keven@softwiki.com.br",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"key": "password",
|
||||
"value": "123",
|
||||
"type": "text"
|
||||
"mode": "raw",
|
||||
"raw": "{\r\n \"data_pagamento\": {\r\n \"date_start\": \"2024-01-01\",\r\n \"date_end\": \"2024-02-01\"\r\n }\r\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{BaseUrlV1}}administrativo/caixa",
|
||||
|
|
@ -74,7 +68,7 @@
|
|||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\r\n \"especie_pagamento\": \"D\",\r\n \"caixa_item_id\" : 12,\r\n \"caixa_servico_id\": 9,\r\n \"usuario_servico_id\": 123456,\r\n \"usuario_caixa_id\": null,\r\n \"chave_servico\": null,\r\n \"descricao\": \"1\",\r\n \"data_pagamento\": null,\r\n \"situacao\": \"3\",\r\n \"tipo_documento\": \"C\",\r\n \"tipo_transacao\": \"C\",\r\n \"valor_servico\": 61,\r\n \"valor_pago\": 61,\r\n \"observacao\": null,\r\n \"caixa_cheque_id\": null,\r\n \"hora_pagamento\": null,\r\n \"caixa_id\": null,\r\n \"recibo_id\": null,\r\n \"tipo_servico\": \"17\",\r\n \"qtd\": 1,\r\n \"apresentante\": \"1\",\r\n \"mensalista_id\": null,\r\n \"quitado_caixa_id\": null,\r\n \"registrado\": 1,\r\n \"emolumento\": 33,\r\n \"taxa_judiciaria\": 14,\r\n \"fundesp\": 13,\r\n \"desconto\": 0,\r\n \"valor_documento\": 0,\r\n \"outra_taxa1\": 0,\r\n \"chave_servico_sec\": null,\r\n \"emolumento_item_id\": null,\r\n \"caixa_registroselo_id\": null,\r\n \"fundo_ri\": null,\r\n \"valor_recibo\": null,\r\n \"boleto_pdf\": null,\r\n \"boleto_vencimento\": null,\r\n \"iss\": 1,\r\n \"nlote\": 0,\r\n \"tabela\": \"1\",\r\n \"campo_id\": 5,\r\n \"boleto_id\": null,\r\n \"valor_adicional\": null,\r\n \"pix_id\": null\r\n}",
|
||||
"raw": "{\r\n \"especie_pagamento\": \"D\",\r\n \"caixa_item_id\": 275547.00,\r\n \"caixa_servico_id\": 2.00,\r\n \"usuario_servico_id\": 123456.00,\r\n \"usuario_caixa_id\": 123456.00,\r\n \"descricao\": \"{{$randomProductName}}\",\r\n \"data_pagamento\": \"2025-07-02 00:00:00.000\",\r\n \"situacao\": \"4\",\r\n \"tipo_documento\": \"C\",\r\n \"tipo_transacao\": \"C\",\r\n \"valor_servico\": 123123.000,\r\n \"valor_pago\": 123123.000,\r\n \"observacao\": \"{{$randomJobDescriptor}}\",\r\n \"hora_pagamento\": \"12:58\",\r\n \"tipo_servico\": \"25\",\r\n \"registrado\": \"3\"\r\n}\r\n",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
|
|
@ -127,13 +121,13 @@
|
|||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{BaseUrlV1}}administrativo/caixa/3",
|
||||
"raw": "{{BaseUrlV1}}administrativo/caixa/27551",
|
||||
"host": [
|
||||
"{{BaseUrlV1}}administrativo"
|
||||
],
|
||||
"path": [
|
||||
"caixa",
|
||||
"3"
|
||||
"27551"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
|||
6
server.bat
Normal file
6
server.bat
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
@echo off
|
||||
cd \
|
||||
cd C:\IIS\Orius\Api
|
||||
call .venv\Scripts\activate.bat
|
||||
uvicorn main:app --reload
|
||||
pause
|
||||
Loading…
Add table
Reference in a new issue