Skip to content

Commit

Permalink
RED-16 - criação de conta - email de confirmação
Browse files Browse the repository at this point in the history
  • Loading branch information
sombriks committed Sep 5, 2024
1 parent 02a6010 commit 3a8acd3
Show file tree
Hide file tree
Showing 39 changed files with 372 additions and 175 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

/**
* confirm user creation table
*
* - all user data
* - confirm expiration time
* - six digit challenge code for this
*
* @param { import("knex").Knex } knex
* @returns { Promise<void>|import("knex").Knex.SchemaBuilder }
*/
export const up = async (knex) => {
return knex.schema.createTable('confirma_cadastro', tb => {
tb.increments()
tb.string('nome').notNullable()
tb.string('email').notNullable()
tb.string('senha').notNullable()
tb.string('challenge').notNullable()
tb.timestamp("criacao").notNullable().defaultTo(knex.fn.now())
tb.timestamp("alteracao").notNullable().defaultTo(knex.fn.now())
tb.boolean('enviado').notNullable().defaultTo(false)
tb.boolean('consumido').notNullable().defaultTo(false)
})
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void>|import("knex").Knex.SchemaBuilder }
*/
export const down = async (knex) => {
return knex.schema.dropTable('confirma_cadastro');
};
2 changes: 2 additions & 0 deletions service-node-koa/app/config/security/encryption.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ export const sign = (payload) => {
export const verify = (token) => {
return jwt.verify(token?.token || token, process.env.SECRET);
};

export const newChallenge = () => randomBytes(3).toString('hex')
27 changes: 26 additions & 1 deletion service-node-koa/app/controllers/usuario.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
delUsuario,
getById,
login,
novoUsuario,
resetCategorias,
Expand All @@ -9,6 +8,16 @@ import {
validaInvite,
} from "../services/index.mjs";
import {sign} from "../config/security/index.mjs";
import {
marcarConsumoConfirmaCadastro,
marcarEnvioConfirmaCadastro,
novoConfirmaCadastro,
validaConfirmaCadastro
} from "../services/confirma_cadastro.mjs";
import {emailConfirmaCadastro} from "../services/email.mjs";
import {logger} from "../config/base-logging.mjs";

const log = logger.scope('controllers/usuario.mjs')

export const userLoginRequest = async (ctx) => {
// TODO captcha protection
Expand All @@ -29,10 +38,26 @@ export const userSignupRequest = async (ctx) => {
await resetConta({usuario_id: id});
ctx.body = {id, nome, email, created: true};
} else {
// email challenge
await validaConfirmaCadastro({email})
const [{challenge}] = await novoConfirmaCadastro({nome, email, senha});
const result = await emailConfirmaCadastro({email, nome, challenge})
log.info(result)
await marcarEnvioConfirmaCadastro({email, challenge})
ctx.body = {message: "check mail for activation code!"}
}
};

export const userConfirmRequest = async (ctx) => {
// TODO captcha protection
const {email, challenge} = ctx.request.body;
const {nome, senha} = await marcarConsumoConfirmaCadastro({email, challenge});
const [{id}] = await novoUsuario({nome, email, senha, pwdEncrypt: false});
await resetCategorias({usuario_id: id});
await resetConta({usuario_id: id});
ctx.body = {id, nome, email, created: true};
}

export const delUsuarioRequest = async (ctx) => {
const {email, senha} = ctx.request.query;
const usuario = await login({email, senha});
Expand Down
3 changes: 2 additions & 1 deletion service-node-koa/app/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
updatePlanejamentoRequest,
updateRecorrenciaRequest,
updateUserRequest,
uploadMovimentacoesRequest,
uploadMovimentacoesRequest, userConfirmRequest,
userLoginRequest,
userSignupRequest,
} from "./controllers/index.mjs";
Expand Down Expand Up @@ -66,6 +66,7 @@ new ApiBuilder({router})

b.post("/login", userLoginRequest);
b.post("/signup", userSignupRequest);
b.put("/confirma-cadastro", userConfirmRequest);

b.path("/:usuario_id", ifAuthenticated, (b) => {
b.del("/removeAccount", delUsuarioRequest);
Expand Down
34 changes: 34 additions & 0 deletions service-node-koa/app/services/confirma_cadastro.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {knex} from "../config/db/index.mjs";
import {logger} from "../config/base-logging.mjs";
import {encrypt, newChallenge} from "../config/security/index.mjs";
import {differenceInMinutes} from "date-fns";

const log = logger.scope('services/confirma_cadastro.mjs')

export const validaConfirmaCadastro = async ({email}) => {
log.info('validaConfirmaCadastro')
const exists = await knex("usuario").where({email}).first();
if (exists) throw {status: 422, message: "Usuario já existe"};
}

export const novoConfirmaCadastro = async ({nome, email, senha}) => {
log.info('novoConfirmaCadastro')
const challenge = newChallenge()
senha = encrypt(senha)
return knex("confirma_cadastro").insert({nome, email, senha, challenge}).returning("*")
}

export const marcarEnvioConfirmaCadastro = async ({email, challenge}) => {
log.info('marcarEnvioConfirmaCadastro')
return knex("confirma_cadastro").update({enviado: true, alteracao: new Date()}).where({email, challenge});
}

export const marcarConsumoConfirmaCadastro = async ({email, challenge}) => {
log.info('marcarConsumoConfirmaCadastro')
const confirmaCadastro = await knex("confirma_cadastro").where({email, challenge}).first();
if (!confirmaCadastro) throw {code: 422, message: 'Código ou email incorretos'}
if (confirmaCadastro.consumido) throw {code: 422, message: 'Código já foi utilizado'}
if (differenceInMinutes(new Date(), confirmaCadastro.criado) > 5) throw {code: 422, message: 'Código expirado'}
await knex("confirma_cadastro").update({consumido: true}).where({email, challenge});
return confirmaCadastro
}
45 changes: 33 additions & 12 deletions service-node-koa/app/services/email.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
import {
emailApiUrl,
emailCreds,
singlePayload,
emailApiUrl,
emailCreds,
singlePayload,
} from "../config/email-config.mjs";
import {logger} from "../config/base-logging.mjs";

const log = logger.scope('services/email.mjs')

// https://developer.mozilla.org/pt-BR/docs/Web/API/Window/fetch
const sendEmail = async ({ email, name, subject, body }) => {
return await fetch(emailApiUrl, {
body: JSON.stringify(singlePayload({ email, name, subject, body })),
headers: {
"Content-Type": "application/json",
Authorization: emailCreds(),
},
method: "POST",
});
const sendEmail = async ({email, nome, subject, body}) => {
log.info('sendEmail')
// TODO send to a queue instead
return await fetch(emailApiUrl, {
body: JSON.stringify(singlePayload({email, name: nome, subject, body})),
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${emailCreds()}`,
},
method: "POST",
});
};

export const emailConfirmaCadastro = async ({email, nome, challenge}) => {
log.info('emailConfirmaCadastro')
const subject = "Redline - Confirmação de cadastro"
return sendEmail(({
nome, email, subject, body: `
Olá ${nome}!
O código de confirmação da criação de conta é:
${challenge}
Informe este código no aplicativo e ative a sua conta.
`
}))
}
14 changes: 8 additions & 6 deletions service-node-koa/app/services/invite.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { knex } from "../config/db/index.mjs";
import {knex} from "../config/db/index.mjs";

export const validaInvite = async ({ email, code }) => {
if (!email) throw { status: 401, message: "Invalid email" };
if (!code) throw { status: 401, message: "Invalid invite code" };
export const validaInvite = async ({email, code}) => {
if (!email) throw {status: 401, message: "Email inválido"};
if (!code) throw {status: 401, message: "Código de convite inválido"};

const invite = await knex("invite").where({ email, code }).first();
const invite = await knex("invite").where({email, code}).first();
if (!invite) throw {status: 401, message: "Convite não encontrado"};

if (!invite) throw { status: 401, message: "Invite code not found" };
const exists = await knex("usuario").where({email}).first();
if (exists) throw {status: 422, message: "Usuario já existe"};
};
6 changes: 4 additions & 2 deletions service-node-koa/app/services/usuario.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { knex } from "../config/db/index.mjs";
import { decrypt, encrypt } from "../config/security/index.mjs";

export const novoUsuario = async ({ nome, email, senha }) =>
knex("usuario").insert({ nome, email, senha: encrypt(senha) }, ["id"]);
export const novoUsuario = async ({ nome, email, senha, pwdEncrypt = true }) => {
senha = pwdEncrypt ? encrypt(senha) : senha
return knex("usuario").insert({ nome, email, senha }, ["id"]);
}

export const delUsuario = async (id = -1) =>
knex("usuario").del().where({ id });
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ import { computed, defineModel } from 'vue'
import { prepareDate, prepareMoney } from '@/services/formatters'
import { useContaStore } from '@/stores/contaStore'
import { useCategoriaStore } from '@/stores/categoriaStore'
import ChipCategoria from '@/shared/chip-categoria.vue'
import ChipConta from '@/shared/chip-conta.vue'
import ChipDescricao from '@/shared/chip-descricao.vue'
import ChipCategoria from '@/controls/chip-categoria.vue'
import ChipConta from '@/controls/chip-conta.vue'
import ChipDescricao from '@/controls/chip-descricao.vue'
import { isBefore } from 'date-fns'
const mode = defineModel('mode', { default: 0 })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
<script setup>
import { computed, defineModel, ref } from 'vue'
import { prepareDate } from '@/services/formatters'
import ChipDate from '@/shared/chip-date.vue'
import ChipDate from '@/controls/chip-date.vue'
import { addMonths, addYears } from 'date-fns'

const props = defineProps(['label', 'reset'])
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<script setup>
import { ref, watch } from 'vue'
import { useContaStore } from '@/stores/contaStore'
import ChipConta from '@/shared/chip-conta.vue'
import ChipConta from '@/controls/chip-conta.vue'
const contaState = useContaStore()
const props = defineProps(['modelValue', 'rules'])
Expand Down
2 changes: 1 addition & 1 deletion web-app-vue/src/pages/categoria/detalhe-categoria.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<script setup>
import { reactive, ref } from 'vue'
import { requiredRule } from '@/services/basic-rules'
import ChipCategoria from '@/shared/chip-categoria.vue'
import ChipCategoria from '@/controls/chip-categoria.vue'
const props = defineProps(['categoria'])
const emit = defineEmits(['onRemove', 'onEdit'])
Expand Down
4 changes: 2 additions & 2 deletions web-app-vue/src/pages/configuracao/card-configuracoes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ import { downloadCsv, uploadCsv } from '@/services/api'
import { useContaStore } from '@/stores/contaStore'
import { useCategoriaStore } from '@/stores/categoriaStore'
import { useMovimentacaoStore } from '@/stores/movimentacaoStore'
import ContaAutocomplete from '@/shared/conta-autocomplete.vue'
import ContaAutocomplete from '@/controls/conta-autocomplete.vue'
import { endOfMonth, startOfMonth } from 'date-fns'
import ChipPeriodo from '@/shared/chip-periodo.vue'
import ChipPeriodo from '@/controls/chip-periodo.vue'
const router = useRouter()
Expand Down
2 changes: 1 addition & 1 deletion web-app-vue/src/pages/conta/detalhe-conta.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
import { computed, reactive, ref } from 'vue'
import { useContaStore } from '@/stores/contaStore'
import { dayOfMonthRule, numberRule, requiredRule } from '@/services/basic-rules'
import ChipConta from '@/shared/chip-conta.vue'
import ChipConta from '@/controls/chip-conta.vue'
const cState = useContaStore()
Expand Down
2 changes: 1 addition & 1 deletion web-app-vue/src/pages/dashboard/controles-dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import { endOfMonth, startOfMonth } from 'date-fns'
import { onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import ChipPeriodo from '@/shared/chip-periodo.vue'
import ChipPeriodo from '@/controls/chip-periodo.vue'
import { useDashboardStore } from '@/stores/dashboardStore'
import Saldos from '@/pages/dashboard/graficos/saldos.vue'
import Vencimentos from '@/pages/dashboard/graficos/vencimentos.vue'
Expand Down
2 changes: 1 addition & 1 deletion web-app-vue/src/pages/dashboard/graficos/saldos.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
/>
</template>
<script setup>
import ChipValor from '@/shared/chip-valor.vue'
import ChipValor from '@/controls/chip-valor.vue'
import { useDashboardStore } from '@/stores/dashboardStore'
const dashboardState = useDashboardStore()
Expand Down
6 changes: 3 additions & 3 deletions web-app-vue/src/pages/movimentacao/editar-movimentacao.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ import { useMovimentacaoStore } from '@/stores/movimentacaoStore'
import { useContaStore } from '@/stores/contaStore'
import { useCategoriaStore } from '@/stores/categoriaStore'
import { numberRule, requiredRule } from '@/services/basic-rules'
import ChipDate from '@/shared/chip-date.vue'
import ContaAutocomplete from '@/shared/conta-autocomplete.vue'
import CategoriaAutocomplete from '@/shared/categoria-autocomplete.vue'
import ChipDate from '@/controls/chip-date.vue'
import ContaAutocomplete from '@/controls/conta-autocomplete.vue'
import CategoriaAutocomplete from '@/controls/categoria-autocomplete.vue'
const props = defineProps(['movimentacao'])
Expand Down
12 changes: 6 additions & 6 deletions web-app-vue/src/pages/movimentacao/lista-movimentacoes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,12 @@ import { useCategoriaStore } from '@/stores/categoriaStore'
import { useContaStore } from '@/stores/contaStore'
import { prepareBalance, prepareDate } from '@/services/formatters'
import { endOfMonth, format, startOfMonth } from 'date-fns'
import CategoriaAutocomplete from '@/shared/categoria-autocomplete.vue'
import ContaAutocomplete from '@/shared/conta-autocomplete.vue'
import ChipSaldo from '@/shared/chip-saldo-movimentacao.vue'
import ChipConta from '@/shared/chip-conta.vue'
import ChipPeriodo from '@/shared/chip-periodo.vue'
import ChipMovimentacao from '@/shared/chip-movimentacao.vue'
import CategoriaAutocomplete from '@/controls/categoria-autocomplete.vue'
import ContaAutocomplete from '@/controls/conta-autocomplete.vue'
import ChipSaldo from '@/controls/chip-saldo-movimentacao.vue'
import ChipConta from '@/controls/chip-conta.vue'
import ChipPeriodo from '@/controls/chip-periodo.vue'
import ChipMovimentacao from '@/controls/chip-movimentacao.vue'

const movimentacaoStore = useMovimentacaoStore()
const categoriaStore = useCategoriaStore()
Expand Down
10 changes: 5 additions & 5 deletions web-app-vue/src/pages/pagamento/pagamento-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@
import { computed, reactive, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { requiredRule } from '@/services/basic-rules'
import ContaAutocomplete from '@/shared/conta-autocomplete.vue'
import ChipPeriodo from '@/shared/chip-periodo.vue'
import ContaAutocomplete from '@/controls/conta-autocomplete.vue'
import ChipPeriodo from '@/controls/chip-periodo.vue'
import { endOfMonth, startOfMonth } from 'date-fns'
import ChipDate from '@/shared/chip-date.vue'
import ChipDate from '@/controls/chip-date.vue'
import { useMovimentacaoStore } from '@/stores/movimentacaoStore'
import ChipDescricao from '@/shared/chip-descricao.vue'
import ChipDescricao from '@/controls/chip-descricao.vue'
import { prepareDate, prepareMoney } from '@/services/formatters'
import CategoriaAutocomplete from '@/shared/categoria-autocomplete.vue'
import CategoriaAutocomplete from '@/controls/categoria-autocomplete.vue'
const router = useRouter()
const movimentacaoStore = useMovimentacaoStore()
Expand Down
6 changes: 3 additions & 3 deletions web-app-vue/src/pages/pagamento/transferencia-form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { numberRule, requiredRule } from '@/services/basic-rules'
import ContaAutocomplete from '@/shared/conta-autocomplete.vue'
import ChipDate from '@/shared/chip-date.vue'
import ContaAutocomplete from '@/controls/conta-autocomplete.vue'
import ChipDate from '@/controls/chip-date.vue'
import { useMovimentacaoStore } from '@/stores/movimentacaoStore'
import CategoriaAutocomplete from '@/shared/categoria-autocomplete.vue'
import CategoriaAutocomplete from '@/controls/categoria-autocomplete.vue'
const router = useRouter()
const movimentacaoStore = useMovimentacaoStore()
Expand Down
4 changes: 2 additions & 2 deletions web-app-vue/src/pages/planejamento/detalhe-planejamento.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@
<script setup>
import { computed, onMounted, ref, toRaw, watch } from 'vue'
import { numberRule, requiredRule } from '@/services/basic-rules'
import CategoriaAutocomplete from '@/shared/categoria-autocomplete.vue'
import CategoriaAutocomplete from '@/controls/categoria-autocomplete.vue'
import { useCategoriaStore } from '@/stores/categoriaStore'
import { endOfYear, startOfYear } from 'date-fns/fp'
import { prepareMoney } from '@/services/formatters'
import ChipPeriodo from '@/shared/chip-periodo.vue'
import ChipPeriodo from '@/controls/chip-periodo.vue'
const categoriaStore = useCategoriaStore()
Expand Down
2 changes: 1 addition & 1 deletion web-app-vue/src/pages/planejamento/lista-planejamentos.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import DetalhePlanejamento from '@/pages/planejamento/detalhe-planejamento.vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { usePlanejamentoStore } from '@/stores/planejamentoStore'
import { useCategoriaStore } from '@/stores/categoriaStore'
import ChipSaldoPlanejamento from '@/shared/chip-saldo-planejamento.vue'
import ChipSaldoPlanejamento from '@/controls/chip-saldo-planejamento.vue'
const drawer = ref(false)
Expand Down
Loading

0 comments on commit 3a8acd3

Please sign in to comment.