O aplicativo possui as operações para fazer o CRUD nas tabelas representadas no modelo a seguir. Essas operações estão disponíveis através de rotas que possuem controle de acesso para usuários logados com o perfil adm, perfil user e sem a necessidade de estar logado. Ao efetuar o login, os dados do usuário são empacotados em um token e retornados para o cliente, que por sua vez, terá de enviar esse token em todas as requisições que requerem o controle de acesso. O token é gerado usando o pacote JWT (JSON Web Token). O código disponível no pacote middlewares é chamado antes da função objetivo da rota para decodificar o token e validar o perfil de acesso do usuário. Se o usuário não tiver permissão, a função middleware impede o acesso ao recurso mapeado pela rota.
Todos os pacotes necessários estão no package.json
.
git clone https://github.com/arleysouza/jwt-postgresql.git server
cd server
npm i
Você precisa substituir as variáveis de ambiente do arquivo .env
pelos parâmetros de conexão do SBGD PostgreSQL que você criou.
PORT = 3010
JWT_SECRET = @tokenJWT
DB_USER = postgres
DB_HOST = localhost
DB_NAME = bdatividade
DB_PASSWORD = 123
DB_PORT = 5432
DB_URI = postgres://root:@dpg-coufst21hbls7385tsd0-a.oregon-postgres.render.com/bdatividade
Se optar pelo SGBD na nuvem, você precisará sustituir a variável DB_URI
e também retirar o comentário do código a seguir no arquivo src/database/connection.ts
, para configurar o pool de conexões.
const pool = new Pool({
connectionString: process.env.DB_URI,
ssl: {
rejectUnauthorized: false
}
});
No arquivo src/database/create.ts
estão as instruções SQL para criar as tabelas e definir os triggers com as validações ao fazer insert e update nas tabelas.
Execute o comando npm run create
para submeter as instruções SQL no SGBD.
Para fazer as validações nos campos das tabelas foram definidos os seguintes triggers, onde cada trigger foi definido usando uma função:
- Trigger de
before insert
na tabelausers
: o comando a seguir faz a vinculação da funçãousers_insert_validade
ao trigger na tabelausers
:
CREATE TRIGGER users_insert_trigger
BEFORE INSERT ON users
FOR EACH ROW EXECUTE PROCEDURE users_insert_validade();
- Trigger de
before update
na tabelausers
: definido usando a funçãousers_update_validade
; - Trigger de
before insert
na tabelaproducts
: definido usando a funçãoproducts_insert_validate
; - Trigger de
before update
na tabelaproducts
: definido usando a funçãoproducts_update_validate
; - Trigger de
before insert
na tabelacategories
: definido usando a funçãocategories_insert_validate
; - Trigger de
before update
na tabelacategories
: definido usando a funçãocategories_update_validate
; - Trigger de
before insert
na tabelaspents
: definido usando a funçãospents_insert_validate
; - Trigger de
before update
na tabelaspents
: definido usando a funçãospents_update_validate
.
As restrições dos campos estão sendo validadas nas funções e serão lançadas exceções no caso de inconformidades.
- A tabela
categories
possui apenas o camponame
com restrições. Será lançada uma exceção caso não seja satisfeita a condição:
CREATE FUNCTION categories_insert_validate() RETURNS trigger AS $$
BEGIN
-- Converte para minúsculo e remove os espaços no início e fim
new.name := lower(trim(new.name));
-- Verifica se o nome já existe
IF EXISTS (SELECT 1 FROM categories WHERE new.name = name) THEN
RAISE EXCEPTION 'O nome % já está em uso', new.name;
ELSE
-- Verifica se o nome possui algum caractere
IF length(new.name) = 0 THEN
RAISE EXCEPTION 'O nome precisa ter pelo menos uma letra';
END IF;
END IF;
RETURN new;
END;
$$ LANGUAGE plpgsql;
- A tabela
products
possui os camposname
eidcategory
com restrições.
CREATE FUNCTION products_insert_validate() RETURNS trigger AS $$
BEGIN
-- Verifica se foi fornecido o nome
IF new.name is null THEN
RAISE EXCEPTION 'O nome é obrigatório';
ELSE
-- Converte para minúsculo e remove os espaços no início e fim
new.name := lower(trim(new.name));
-- Verifica se o nome já existe
IF EXISTS (SELECT 1 FROM products WHERE new.name = name) THEN
RAISE EXCEPTION 'O nome % já está em uso', new.name;
ELSE
-- Verifica se o nome possui algum caractere
IF length(new.name) = 0 THEN
RAISE EXCEPTION 'O nome precisa ter pelo menos uma letra';
ELSE
-- Verifica se foi fornecido o idcategory
IF new.idcategory is NULL THEN
RAISE EXCEPTION 'A categoria é obrigatória';
ELSE
-- Verifica se o idcategory fornecido existe na tabela categories
IF NOT EXISTS (SELECT 1 FROM categories WHERE id = NEW.idcategory) THEN
RAISE EXCEPTION 'A categoria fornecida não existe no cadastro';
END IF;
END IF;
END IF;
END IF;
END IF;
RETURN new;
END;
$$ LANGUAGE plpgsql;
- A tabela
users
possui os camposmail
epassword
com restrições.
CREATE FUNCTION users_insert_validade() RETURNS trigger AS $$
BEGIN
-- Converte para minúsculo e remove os espaços no início e fim
new.mail := lower(trim(new.mail));
new.password := trim(new.password);
-- Verifica se foi fornecido o mail
IF new.mail is null THEN
RAISE EXCEPTION 'O e-mail é obrigatório';
ELSE
-- Verifica se foi fornecida a senha
IF new.password is null THEN
RAISE EXCEPTION 'A senha é obrigatória';
ELSE
-- Valida o formato do e-mail usando expressão regular
IF NOT new.mail ~* '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' THEN
RAISE EXCEPTION 'O e-mail % não está em um formato válido', new.mail;
ELSE
-- Verifica se o mail já existe
IF EXISTS (SELECT 1 FROM users WHERE new.mail = mail) THEN
RAISE EXCEPTION 'O e-mail % já está em uso', new.mail;
ELSE
-- Verifica se a senha tem a quantidade correta de caracteres
IF length(new.password) < 6 OR length(new.password) > 10 THEN
RAISE EXCEPTION 'A senha deve ter entre 6 e 10 caracterese';
END IF;
END IF;
END IF;
END IF;
END IF;
RETURN new;
END;
$$ LANGUAGE plpgsql;
- A tabela
spents
possui os camposvalue
eidproduct
com restrições.
CREATE FUNCTION spents_insert_validate() RETURNS trigger AS $$
BEGIN
-- Verifica se foi fornecido o valor
IF new.value is null THEN
RAISE EXCEPTION 'O valor é obrigatório';
ELSE
-- Verifica se foi fornecido o ID do produto
IF new.idproduct is null THEN
RAISE EXCEPTION 'O produto é obrigatório';
ELSE
-- Verifica se o idproduct existe na tabela products
IF NOT EXISTS (SELECT 1 FROM products WHERE id = NEW.idproduct) THEN
RAISE EXCEPTION 'O produto fornecido não existe no cadastro';
END IF;
END IF;
END IF;
RETURN new;
END;
$$ LANGUAGE plpgsql;
No arquivo src/database/load.ts
estão as instruções SQL para carregar registros nas tabelas.
Execute o comando npm run load
para submeter as instruções SQL no SGBD.
A aplicação possui os níveis de acesso para os perfis adm
e user
.
Rotas sem restrição de acesso:
- HTTP POST
/login
: efetuar login; - HTTP POST
/usuario
: o usuário efetua o seu próprio cadastro.
Rotas para usuário logados:
- HTTP GET
/categoria
: listar as categorias; - HTTP GET
/produto
: listar os produtos; - HTTP GET
/gasto
: usuário lista somente os seus gastos; - HTTP POST
/gasto
: usuário cria um gasto; - HTTP PUT
/gasto
: usuário altera um gasto dele; - HTTP DELETE
/gasto
: usuário exclui um gasto dele; - HTTP DELETE
/usuario
: usuário exclui o próprio cadastro; - HTTP PUT
/usuario/mail
: usuário altera o próprio e-mail; - HTTP PUT
/usuario/senha
: usuário altera a própria senha.
Rotas para usuário logados com o perfil adm
:
- HTTP POST
/categoria
: cria uma categoria; - HTTP PUT
/categoria
: altera uma categoria; - HTTP DELETE
/categoria
: exclui uma categoria; - HTTP POST
/produto
: cria um produto; - HTTP PUT
/produto
: altera um produto; - HTTP DELETE
/produto
: exclui um produto; - HTTP GET
/usuario
: listar os usuários; - HTTP PUT
/usuario/perfil
: altera o perfil de algum usuário.