Como já citado anteriormente, Helpers são contruídos de acordo com a especificação machinepack E NÃO FUNCIONARÃO se forem escritos de outra forma, pois são interpretados pelo core do Sails. Observe que os Hooks e as Policies são funcões JavaScript padrão e não seguem o modelo machinepack.
Helpers devem estar localizados no diretório \api\helpers para que sejam acessíveis
em qualquer parte do seu aplicativo. Utilize await sails.helpers.nomeHelper(params) para
utilizá-los
Hooks devem estar localizados no diretório \api\hooks para que funcionem e serão
executados durante a inicialização.
Policies devem estar localizadas no diretório \api\policies para que sejam
localizadas
pelo core Sails enquanto executa o arquivo de configuracao config/policies.js, durante a inicialização da aplicação.
Seguem abaixo alguns exemplos funcionais que pode utilizar livremente:
/* is-jwt-valid.js (Policy)
╔╗ ╔╗╔═══╗╔╗ ╔══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔╗╔╗╔╗╔╗╔════╗
║╚╗╔╝║║╔═╗║║║ ╚╣╠╝╚╗╔╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ║║║║║║║║║╔╗╔╗║
╚╗║║╔╝║║ ║║║║ ║║ ║║║║║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║║║║╚╝║║╚╝
║╚╝║ ║╚═╝║║║ ╔╗ ║║ ║║║║║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ╔╗║║║╚╝╚╝║ ║║
╚╗╔╝ ║╔═╗║║╚═╝║╔╣╠╗╔╝╚╝║║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ║╚╝║╚╗╔╗╔╝ ╔╝╚╗
╚╝ ╚╝ ╚╝╚═══╝╚══╝╚═══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚══╝ ╚╝╚╝ ╚══╝
*/
let jwt = require('jsonwebtoken')
module.exports = async function (req, res, proceed) {
let token = req.headers['authorization'] + ''
if (token && token.startsWith('Bearer')) {
token = token.substring(7)
/* verifica se o TOKEN recebido esta valido. */
/* o TOKEN foi gerado anteriormente usando a */
/* mesma JWT_SECRET */
jwt.verify(token, process.env.JWT_SECRET, function (err, decoded) {
if (!err) {
return proceed();
} else {
console.log('Unauthorized!')
return res.forbidden();
}
});
} else {
console.log('Invalid Token')
return res.forbidden();
}
};
/* is-auth-basic-valid.js (Policy)
╔╗ ╔╗╔═══╗╔╗ ╔══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔╗ ╔╗╔════╗╔╗ ╔╗ ╔══╗ ╔═══╗╔═══╗╔══╗╔═══╗
║╚╗╔╝║║╔═╗║║║ ╚╣╠╝╚╗╔╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ║╔═╗║║║ ║║║╔╗╔╗║║║ ║║ ║╔╗║ ║╔═╗║║╔═╗║╚╣╠╝║╔═╗║
╚╗║║╔╝║║ ║║║║ ║║ ║║║║║║ ║║ ║║║║║║ ║║║╚═╝║ ║║ ║║║║ ║║╚╝║║╚╝║╚═╝║ ║╚╝╚╗║║ ║║║╚══╗ ║║ ║║ ╚╝
║╚╝║ ║╚═╝║║║ ╔╗ ║║ ║║║║║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║╚═╝║║║ ║║ ║║ ║╔═╗║ ║╔═╗║║╚═╝║╚══╗║ ║║ ║║ ╔╗
╚╗╔╝ ║╔═╗║║╚═╝║╔╣╠╗╔╝╚╝║║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ║╔═╗║║╚═╝║ ╔╝╚╗ ║║ ║║ ║╚═╝║║╔═╗║║╚═╝║╔╣╠╗║╚═╝║
╚╝ ╚╝ ╚╝╚═══╝╚══╝╚═══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚╝ ╚╝╚═══╝ ╚══╝ ╚╝ ╚╝ ╚═══╝╚╝ ╚╝╚═══╝╚══╝╚═══╝
*/
module.exports = async function (req, res, proceed) {
let auth = req.headers['authorization'] + ''
if (auth && auth.startsWith('Basic ')) {
auth = auth.substring(6)
let decoded = window.atob(auth);
let splited = decoded.split(`:`)
let login = splited[0]
let password = splited[1]
/* adicione aqui seu algoritimo de verificacao */
/* do login e senha contra a informacao de seu BD */
/* retornando 'false' se a verificacao falhar */
return false
} else {
console.log('Invalid Token')
return res.forbidden();
}
};
/* gerador-dv.js (Helper)
╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔═══╗ ╔═══╗╔══╗╔═══╗ ╔╗ ╔╗╔═══╗╔═══╗╔══╗ ╔═══╗╔══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗
║╔═╗║║╔══╝║╔═╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ╚╗╔╗║║╔══╝ ╚╗╔╗║╚╣╠╝║╔═╗║ ║╚╗╔╝║║╔══╝║╔═╗║╚╣╠╝ ║╔══╝╚╣╠╝║╔═╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║
║║ ╚╝║╚══╗║╚═╝║║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║╚══╗ ║║║║ ║║ ║║ ╚╝ ╚╗║║╔╝║╚══╗║╚═╝║ ║║ ║╚══╗ ║║ ║║ ╚╝║║ ║║ ║║║║║║ ║║║╚═╝║
║║╔═╗║╔══╝║╔╗╔╝║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║║║║║╔══╝ ║║║║ ║║ ║║╔═╗ ║╚╝║ ║╔══╝║╔╗╔╝ ║║ ║╔══╝ ║║ ║║ ╔╗║╚═╝║ ║║║║║║ ║║║╔╗╔╝
║╚╩═║║╚══╗║║║╚╗║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ╔╝╚╝║║╚══╗ ╔╝╚╝║╔╣╠╗║╚╩═║╔╗ ╚╗╔╝ ║╚══╗║║║╚╗╔╣╠╗╔╝╚╗ ╔╣╠╗║╚═╝║║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗
╚═══╝╚═══╝╚╝╚═╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚═══╝╚═══╝ ╚═══╝╚══╝╚═══╝╚╝ ╚╝ ╚═══╝╚╝╚═╝╚══╝╚══╝ ╚══╝╚═══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝
*/
module.exports = {
friendlyName: 'Gerador de Digito Verificador',
description: 'Calcula o valor de um determinado digito verificador.',
extendedDescription:
`Calcula o valor de um determinado digito verificador (DV) a partir dos pesos relativos
aplicáveis ao cálculo. Se aplica tanto para CNPJ quanto para CPF, não se limitando a estes.
Utilize os pesos: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] para CPF e [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6] para CNPJ `,
inputs: {
digits: {type: 'string', required: true, description: 'Digitos ainda nao validados (sem o DV)', example: '316675800001'},
weights: {type: 'ref', required: true, description: 'Array de pesos relativos atribuídos ao calculo', example: '[2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6]'},
},
exits: {
success: {
description: 'Calculo realizado com sucesso'
},
error: {
description: 'Erro ao tentar calcular o DV',
}
},
fn: async function ({digits, weights}) {
const digitsLength = digits.length;
const digitsLengthWithoutChecker = weights.length - 1;
const sum = digits.split('').reduce((acc, digit, idx) => {
return acc + +digit * weights[digitsLength - 1 - idx];
}, 0);
const sumDivisionRemainder = sum % 11;
const checker = sumDivisionRemainder < 2 ? 0 : 11 - sumDivisionRemainder;
if (digitsLength === digitsLengthWithoutChecker) {
return await sails.helpers.geradorDv(`${digits}${checker}`, weights);
}
return `${digits[digitsLength - 1]}${checker}`;
}
};
/* formata-cnpj-cpf.js (Helper)
╔═══╗╔═══╗╔═══╗╔═╗╔═╗╔═══╗╔════╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔═╗ ╔╗╔═══╗ ╔╗ ╔═══╗╔═══╗ ╔═══╗
║╔══╝║╔═╗║║╔═╗║║║╚╝║║║╔═╗║║╔╗╔╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ║╔═╗║║║╚╗║║║╔═╗║ ║║ ║╔═╗║║╔═╗║ ║╔══╝
║╚══╗║║ ║║║╚═╝║║╔╗╔╗║║║ ║║╚╝║║╚╝║║ ║║ ║║║║║║ ║║║╚═╝║ ║║ ╚╝║╔╗╚╝║║╚═╝║ ║║ ║║ ╚╝║╚═╝║ ║╚══╗
║╔══╝║║ ║║║╔╗╔╝║║║║║║║╚═╝║ ║║ ║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║║ ╔╗║║╚╗║║║╔══╝╔╗║║ ╔═══╗ ║║ ╔╗║╔══╝ ║╔══╝
╔╝╚╗ ║╚═╝║║║║╚╗║║║║║║║╔═╗║ ╔╝╚╗ ║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ║╚═╝║║║ ║║║║║ ║╚╝║ ╚═══╝ ║╚═╝║║║ ╔╝╚╗
╚══╝ ╚═══╝╚╝╚═╝╚╝╚╝╚╝╚╝ ╚╝ ╚══╝ ╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚═══╝╚╝ ╚═╝╚╝ ╚══╝ ╚═══╝╚╝ ╚══╝
*/
module.exports = {
friendlyName: 'Formata CNPJ ou CPF Brasileiros',
description: 'Recebe uma sequencia de numeros sem pontos ou traços e retorna formatado',
inputs: {
tipo: {type: 'string', required: true, example: 'CNPJ', description: 'Pode ser CNPJ ou CPF'},
digitos: {
type: 'string',
required: true,
example: '01459385730',
description: 'Pode ser CNPJ com 14 posições ou CPF com 11 posicões'
}
},
exits: {
success: {
description: 'OK.',
},
},
fn: async function ({tipo, digitos}) {
tipo = tipo + ''.toUpperCase();
digitos = digitos + '';
if(tipo!=='CNPJ'&&tipo!=='CPF') {return 'Tipo Invalido. Informe CNPJ ou CPF';}
/* Assume CPF como padrao */
let correctDigitsLength = 11;
let firstDotPosition = 2;
let secondDotPosition = 5;
let slashPosition = -1;
let dashPosition = 8;
if (tipo === 'CNPJ') {
correctDigitsLength = 14;
firstDotPosition = 1;
secondDotPosition = 4;
slashPosition = 7;
dashPosition = 11;
}
if (digitos.length < 11 || digitos.length > 14) {
return `O numero informado ${digitos} deve ter no minimo 11 e no maximo 14 digitos: `;
} else {
const cleanDigits = digitos.replace(/\D/g, '');
return cleanDigits
.slice(0, correctDigitsLength)
.split('')
.reduce((acc, digit, idx) => {
const result = `${acc}${digit}`;
if (idx !== digitos.length - 1) {
if (idx === firstDotPosition || idx === secondDotPosition) {
return `${result}.`;
}
if (idx === slashPosition) {
return `${result}/`;
}
if (idx === dashPosition) {
return `${result}-`;
}
}
return result;
}, '');
}
}
};
/* jwt-login.js (Helper)
╔═══╗╔╗ ╔╗╔════╗╔═══╗╔═╗ ╔╗╔════╗╔══╗╔═══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔╗╔╗╔╗╔╗╔════╗
║╔═╗║║║ ║║║╔╗╔╗║║╔══╝║║╚╗║║║╔╗╔╗║╚╣╠╝║╔═╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ║║║║║║║║║╔╗╔╗║
║║ ║║║║ ║║╚╝║║╚╝║╚══╗║╔╗╚╝║╚╝║║╚╝ ║║ ║║ ╚╝║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║║║║╚╝║║╚╝
║╚═╝║║║ ║║ ║║ ║╔══╝║║╚╗║║ ║║ ║║ ║║ ╔╗║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ╔╗║║║╚╝╚╝║ ║║
║╔═╗║║╚═╝║ ╔╝╚╗ ║╚══╗║║ ║║║ ╔╝╚╗ ╔╣╠╗║╚═╝║║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ║╚╝║╚╗╔╗╔╝ ╔╝╚╗
╚╝ ╚╝╚═══╝ ╚══╝ ╚═══╝╚╝ ╚═╝ ╚══╝ ╚══╝╚═══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚══╝ ╚╝╚╝ ╚══╝
*/
let jwt = require('jsonwebtoken');
module.exports = {
friendlyName: 'Login e Autenticacao por Token (JWT)',
description: 'Recebe uma requisição HTTP contendo como parametros de query (?name=xyz), valida a senha e retorna um JWT',
extendedDescription: `!! Se faz necessario desabilitar o atributo CSRF no dicionario exportado em [security.js], !!
!! caso contrario, nao funcionara, sendo retornado "forbidden" !!`,
inputs: {
emailAddress: { type: 'string', required: true, description: 'email utilizado pelo Usuário na criação do mesmo' },
password: { type: 'string', required: true, description: 'senha em texto claro' },
},
exits: {
success: {
description: 'Autenticado e retornado um JWT Assinado'
},
},
fn: async function ({ emailAddress, password }) {
// procura pelo email informado acima, no banco de dados.
// (observe que o 'lowerCase' garante que a pesquisa seja 'case insensitive',
// independentemente do banco de dados que se esta utilizando)
// Observação: User deve estar definido no arquivo 'model/user.js'!
let userRecord = await User.findOne({
emailAddress: emailAddress.toLowerCase(),
});
// Se nenhum User for encontrado, então dispare o erro de saída "badCombo".
if (!userRecord) {
throw new Error('Not Found');
}
// Se o usuário existe, mas a senha está errada, dispare o erro "badCombo".
// Observe que o método 'checkPassword' deve estar definido no diretório /api/helpers
await sails.helpers.passwords.checkPassword(password, userRecord.password)
.intercept('incorrect', 'badCombo');
return {
token: (jwt.sign({
id: userRecord.id,
exp: Math.floor(Date.now() / 1000) + (60 * 60),
sub: userRecord.emailAddress,
rol: [...userRecord.rules]
}, process.env.JWT_SECRET))
};
// JWT_SECRET deve ter sido previamente exportado para o ambiente Node.js
}
};
/* send-twillio-sms.js (Helper)
╔═══╗╔═╗ ╔╗╔╗ ╔╗╔══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔═══╗ ╔═══╗╔═╗╔═╗╔═══╗
║╔══╝║║╚╗║║║╚╗╔╝║╚╣╠╝║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ╚╗╔╗║║╔══╝ ║╔═╗║║║╚╝║║║╔═╗║
║╚══╗║╔╗╚╝║╚╗║║╔╝ ║║ ║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║╚══╗ ║╚══╗║╔╗╔╗║║╚══╗
║╔══╝║║╚╗║║ ║╚╝║ ║║ ║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║║║║║╔══╝ ╚══╗║║║║║║║╚══╗║
║╚══╗║║ ║║║ ╚╗╔╝ ╔╣╠╗║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ╔╝╚╝║║╚══╗ ║╚═╝║║║║║║║║╚═╝║
╚═══╝╚╝ ╚═╝ ╚╝ ╚══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚═══╝╚═══╝ ╚═══╝╚╝╚╝╚╝╚═══╝
*/
const accountSid = process.env.TWILLIO_ACCOUNT_SID;
const authToken = process.env.TWILLIO_AUTH_TOKEN;
const twillioNumber = process.env.TWILLIO_PHONE_NUMBER
const client = require('twilio')(accountSid, authToken);
module.exports = {
friendlyName: 'Send SMS',
description: 'Send a SMS by Twillio provider.',
extendedDescription: 'To get available this SMS feature we should to create an Twillio account at https://www.twilio.com/try-twilio/?utm_source=sendgrid&utm_medium=consoledash',
inputs: {
to: {type: 'string', required: true},
body: {type: 'string', required: true}
},
exits: {
success: {
description: 'SMS sent by Twillio successfully!'
},
error: {
description: 'If some kind of err occur, it often is due to credentials issues',
}
},
fn: async function ({to, body}) {
console.log(`Enviando SMS para ${to}...`)
let message = await client.messages
.create({
body: body,
from: twillioNumber,
to: to
})
console.log(message.sid)
}
}
/* format-brazilian-date.js (Helper)
╔═══╗╔═══╗╔═══╗╔═╗╔═╗╔═══╗╔════╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔═══╗ ╔═══╗╔═══╗╔════╗╔═══╗ ╔══╗ ╔═══╗
║╔══╝║╔═╗║║╔═╗║║║╚╝║║║╔═╗║║╔╗╔╗║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ╚╗╔╗║║╔══╝ ╚╗╔╗║║╔═╗║║╔╗╔╗║║╔═╗║ ║╔╗║ ║╔═╗║
║╚══╗║║ ║║║╚═╝║║╔╗╔╗║║║ ║║╚╝║║╚╝║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║╚══╗ ║║║║║║ ║║╚╝║║╚╝║║ ║║ ║╚╝╚╗║╚═╝║
║╔══╝║║ ║║║╔╗╔╝║║║║║║║╚═╝║ ║║ ║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║║║║║╔══╝ ║║║║║╚═╝║ ║║ ║╚═╝║ ║╔═╗║║╔╗╔╝
╔╝╚╗ ║╚═╝║║║║╚╗║║║║║║║╔═╗║ ╔╝╚╗ ║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ╔╝╚╝║║╚══╗ ╔╝╚╝║║╔═╗║ ╔╝╚╗ ║╔═╗║ ║╚═╝║║║║╚╗
╚══╝ ╚═══╝╚╝╚═╝╚╝╚╝╚╝╚╝ ╚╝ ╚══╝ ╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚═══╝╚═══╝ ╚═══╝╚╝ ╚╝ ╚══╝ ╚╝ ╚╝ ╚═══╝╚╝╚═╝
*/
module.exports = {
friendlyName: 'Format brazilian date',
description: '',
inputs: {
source: {type: 'string'},
},
exits: {
success: {
description: 'All done.',
},
},
fn: async function ({source}) {
return new Date(source).toLocaleString('pt-br')
}
};
/* send-mailgun-email.js (Helper)
╔═══╗╔═╗ ╔╗╔╗ ╔╗╔══╗╔═══╗╔═══╗╔═══╗╔═══╗ ╔═══╗╔═══╗ ╔═╗╔═╗╔═══╗╔══╗╔╗ ╔═══╗╔╗ ╔╗╔═╗ ╔╗
║╔══╝║║╚╗║║║╚╗╔╝║╚╣╠╝║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║ ╚╗╔╗║║╔══╝ ║║╚╝║║║╔═╗║╚╣╠╝║║ ║╔═╗║║║ ║║║║╚╗║║
║╚══╗║╔╗╚╝║╚╗║║╔╝ ║║ ║║ ║║ ║║║║║║ ║║║╚═╝║ ║║║║║╚══╗ ║╔╗╔╗║║║ ║║ ║║ ║║ ║║ ╚╝║║ ║║║╔╗╚╝║
║╔══╝║║╚╗║║ ║╚╝║ ║║ ║╚═╝║ ║║║║║║ ║║║╔╗╔╝ ║║║║║╔══╝ ║║║║║║║╚═╝║ ║║ ║║ ╔╗║║╔═╗║║ ║║║║╚╗║║
║╚══╗║║ ║║║ ╚╗╔╝ ╔╣╠╗║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗ ╔╝╚╝║║╚══╗ ║║║║║║║╔═╗║╔╣╠╗║╚═╝║║╚╩═║║╚═╝║║║ ║║║
╚═══╝╚╝ ╚═╝ ╚╝ ╚══╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝ ╚═══╝╚═══╝ ╚╝╚╝╚╝╚╝ ╚╝╚══╝╚═══╝╚═══╝╚═══╝╚╝ ╚═╝
*/
var api_key = process.env.MAIL_GUN_API_KEY;
var domain = process.env.MAIL_GUN_DOMAIN;
var mailgun = require('mailgun-js')({apiKey: api_key, domain: domain});
module.exports = {
friendlyName: 'Send MailGun',
description: 'Send a Mail by MailGun provider.',
extendedDescription: 'To get available this MailGun feature we should to create an MailGun account at https://signup.mailgun.com/new/signup',
inputs: {
from: {type: 'string', required: true},
to: {type: 'string', required: true},
subject: {type: 'string', required: true},
text: {type: 'string', required: true}
},
exits: {
success: {
description: 'MailGun sent successfully!'
},
error: {
description: 'If some kind of err occur, it often is due to credentials issues',
}
},
fn: async function ({from, to, subject, text}) {
console.log(`Sendind MailGun to ${to}...`)
var data = {
from: from,
to: to,
subject: subject,
text: text // or html: if you wanna send html instead pure txt
};
mailgun.messages().send(data, function (error, body) {
console.log(body);
});
}
}
Componentes são instâncias reutilizáveis do Vue com um nome. Nesse caso,
//╔═══╗╔═══╗╔═╗╔═╗╔═══╗╔═══╗╔═╗ ╔╗╔═══╗╔═╗ ╔╗╔════╗╔═══╗ ╔═══╗╔═══╗╔═══╗╔══╗╔═╗ ╔╗╔═══╗╔═══╗╔═══╗╔═══╗
//║╔═╗║║╔═╗║║║╚╝║║║╔═╗║║╔═╗║║║╚╗║║║╔══╝║║╚╗║║║╔╗╔╗║║╔══╝ ║╔═╗║║╔═╗║║╔═╗║╚╣╠╝║║╚╗║║║╔═╗║╚╗╔╗║║╔═╗║║╔═╗║
//║║ ╚╝║║ ║║║╔╗╔╗║║╚═╝║║║ ║║║╔╗╚╝║║╚══╗║╔╗╚╝║╚╝║║╚╝║╚══╗ ║╚═╝║║║ ║║║║ ╚╝ ║║ ║╔╗╚╝║║║ ║║ ║║║║║║ ║║║╚═╝║
//║║ ╔╗║║ ║║║║║║║║║╔══╝║║ ║║║║╚╗║║║╔══╝║║╚╗║║ ║║ ║╔══╝ ║╔══╝║╚═╝║║║╔═╗ ║║ ║║╚╗║║║╚═╝║ ║║║║║║ ║║║╔╗╔╝
//║╚═╝║║╚═╝║║║║║║║║║ ║╚═╝║║║ ║║║║╚══╗║║ ║║║ ╔╝╚╗ ║╚══╗ ║║ ║╔═╗║║╚╩═║╔╣╠╗║║ ║║║║╔═╗║╔╝╚╝║║╚═╝║║║║╚╗
//╚═══╝╚═══╝╚╝╚╝╚╝╚╝ ╚═══╝╚╝ ╚═╝╚═══╝╚╝ ╚═╝ ╚══╝ ╚═══╝ ╚╝ ╚╝ ╚╝╚═══╝╚══╝╚╝ ╚═╝╚╝ ╚╝╚═══╝╚═══╝╚╝╚═╝
/* \assets\js\components\paginador.js */
/**
*
* -----------------------------------------------------------------------------
* Um paginador para grandes conjuntos de dados:
*
* @property content um array contendo o conjunto de registros, paginado
* @property number o numero da pagina cujo cursor deve estar
* @property size a quantidade de registros maxima esperada, por pagina
* @property total o numero total de registros (filtrados) da base de dados
*
* How to: para incorporar a paginacao:
* <paginador :content="page.content" :number="page.number" :size="page.size" :total="page.totalpages" v-on:paginated="paginate($event)"></paginador>
* -----------------------------------------------------------------------------
*/
parasails.registerComponent('paginador', {
// ╔═╗╦═╗╔═╗╔═╗╔═╗
// ╠═╝╠╦╝║ ║╠═╝╚═╗
// ╩ ╩╚═╚═╝╩ ╚═╝
props: [
'content',
'number',
'size',
'total'
],
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: function () {
return {
cont: [],
num: 0,
siz: 0,
tot: 0
};
},
// ╦ ╦╔╦╗╔╦╗╦
// ╠═╣ ║ ║║║║
// ╩ ╩ ╩ ╩ ╩╩═╝
template: `
<div id='paginador' class='text-center'>
<em class="fa fa-fast-backward m-2" @click='goToFirst()'></em>
<em class="fa fa-backward m-2" @click='goToPrevious()'></em>
<em class="badget m-2">{{+number + 1}}</em>
<em class="fa fa-forward m-2" @click='goToNext()'></em>
<em class="fa fa-fast-forward m-2" @click='goToLast()'></em>
<br>
<em class="fw-bold">[ {{+number * +size}} - {{(+number * +size) + +size }} / {{ (total) }} ]</em>
</div>
`,
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function () {
this.cont = this.content;
this.num = this.number;
this.siz = this.size;
this.tot = this.total;
},
beforeDestroy: function () {
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
goToFirst: function () {
this.num = 0;
this.$emit('paginated', { content: this.cont, number: this.num, size: this.siz, total: this.tot });
},
goToPrevious: function () {
this.num = this.number - 1;
if (this.num < 0) {this.num = 0;}
this.$emit('paginated', { content: this.cont, number: this.num, size: this.siz, total: this.tot });
},
goToNext: function () {
if(this.content.length < this.size) return;
this.num = this.number + 1;
let maxPage = this.total / this.size;
if (this.num > maxPage) {this.num = maxPage;}
this.$emit('paginated', { content: this.cont, number: this.num, size: this.siz, total: this.tot });
},
goToLast: function () {
if(this.content.length < this.size) return;
this.num = this.total / this.size;
this.$emit('paginated', { content: this.cont, number: this.num, size: this.siz, total: this.tot });
},
}
});
Um recurso útil para inclusão de dados falsos na base de dados, para testes
Para criar um Script utilize o comando sails generate script cria-dados-para-teste
Modifique o script/gerador de dados falsos de Churchs, conforme abaixo:
Para executar o Script utilize o comando sails run cria-dados-para-teste
Para incorporar o Script dentro de outro código, utilize await require('<path>/scripts/cria-dados-para-teste').fn()
module.exports = {
// ___ ___ ___ _ ___ _____ ___ ___ ___ _____ ___ ___ _____ ___ ___ ___ _____ ___ _____ ___
//( _ \ ( _ \ | _ \ (_)( _ \ (_ _) ( _ \ ( _ \ ( _ \( _ )| _ \ ( _ \( _ ) ( _ \ ( _ \ ( _ \( _ )( _ \ ( _ )( _ \
//| (_(_)| ( (_)| (_) )| || |_) )/|| | | | ) || (_(_) | ( (_) (_) || (_) )| ( (_) (_) | | | ) || (_(_) | | ) | (_) || | ) || ( ) || (_(_)
// \__ \ | | _ | / | || __/(_)| | | | | )| _)_ | | _( _ )| / | | __( _ ) | | | )| _)_ | | | ) _ )| | | )| | | | \__ \
//( )_) || (_( )| |\ \ | || | | | | |_) || (_( ) | (_( ) | | || |\ \ | |(_ ) | | | | |_) || (_( ) | |_) | | | || |_) || (_) |( )_) |
// \(___)(____/ (_) (_)(_)(_) ( ) (____/ (____/ (____/(_) (_)(_) (_)(____/(_) (_) (____/ (____/ (____/(_) (_)(____/ (_____) \(___)
friendlyName: 'Cria dados para teste',
description: 'Cria dados para teste utilizando a biblioteca faker como geradora de dados',
fn: async function () {
sails.log('Running custom shell script... (`sails run cria-dados-para-teste`)');
// NAO SE ESQUECA DE INSTALAR A BIBLIOTECA COM [npm i @faker-js/faker --save-dev]
const {faker} = require('@faker-js/faker')
// OBSERVE QUE O OBJETO X RELACIONAL User JÁ EXISTE NATIVAMENTE NO Protótipo Sails.js
let users = await User.find()
let user = users[0]
// OBSERVE QUE OS EXEMPLOS DE MODEL ABAIXO NÃO EXISTEM NATIVAMENTE NO SAILS!
// Você deve substituir os Objetos por aqueles que você deseja carregar com dados falsos para teste
// ou criar estes Model(os) com o comando [sails generate model nome-objeto]
let church = await Church.create({
fullName: faker.company.name(),
shortName: faker.company.buzzNoun(),
email: faker.internet.email(),
address: faker.location.streetAddress(),
site: faker.internet.url(),
phone: faker.phone.number(),
linktree: `https://linktr.ee/fake-cuidado-cristao`,
tipo: 'DAUGHTER'
}).fetch()
for (let i = 1; i < 5; i++) {
await Classroom.create({
name: faker.lorem.word()
})
}
for (let i = 1; i < 5; i++) {
await Contribution.create({
dtContribution: new Date(),
value: faker.commerce.price(),
propose: faker.commerce.productDescription(),
userId: user.id
})
}
for (let i = 1; i < 5; i++) {
await Usercare.create({
userId: user.id,
dtContact: new Date(),
record: faker.lorem.paragraph({min: 1, max: 3})
})
}
await UserChurch.create({
churchId: church.id,
userId: user.id,
type: 'CONGREGATION',
dtAssociation: new Date()
})
let classrooms = await Classroom.find()
classrooms.forEach(classroom=>{
UserClassroom.create({
dtAssociation: new Date(),
type: 'CLASSMATE',
userId: user.id
}).then().catch(err=>console.log(err))
})
sails.log('Finished custom shell script... (`sails run cria-dados-para-teste`)');
}
};
// ____ _____ ____ __ __ __ __ __ __ ____ ____ _____ __ ____ __ _ _ ____ __ ____ ____ __ _____
// ( ___)( _ )( _ \( \/ )( )( )( ) /__\ ( _ \(_ _)( _ ) /__\ (_ _) /__\ ( \/ ) ( _ \ /__\ ( _ \ ( _ \ /__\ ( _ )
// )__) )(_)( ) / ) ( )(__)( )(__ /( )\ ) / _)(_ )(_)( /( )\ /\_)( /( )\ ) ( )___/ /( )\ )(_) ) ) / /( )\ )(_)(
// (_) (_____)(_)\_)(_/\/\_)(______)(____)(__)(__)(_)\_)(____)(_____) (__)(__)\____) (__)(__)(_/\_) (__) (__)(__)(____/ (_)\_)(__)(__)(_____)
// <div id='formulario-ajax-padrao' v-cloak >
// <ajax-form action="updatePassword"
// :syncing.sync="syncing"
// :cloud-error.sync="cloudError"
// :form-data="formData"
// :form-rules="formRules"
// :form-errors.sync="formErrors"
// @submitted="submittedForm()">
// <input class="form-control"
// id="password"
// name="password"
// type="password"
// :class="[formErrors.password ? 'is-invalid' : '']"
// v-model.trim="formData.password"
// placeholder="••••••••"
// autocomplete="new-password"
// focus-first>
// <cloud-error v-if="cloudError"></cloud-error>
// <ajax-button type="submit" :syncing="syncing" class="btn btn-dark">Save changes</ajax-button>
// </ajax-form>
// </div>
parasails.registerPage('formulario-ajax-padrao', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
// Flag de sincronismo de estado (em execucao = true/aguardando = false).
syncing: false,
// Atributos/Dados editaveis do formulario que nao necessitam de validacao alguma
formData: {
rememberMe: true,
},
// Erros encontrados na validacao do formulario.
// Internamente, o Sails grava {invalido = 'true' ou 'false'} para cada campo do formData.
formErrors: { /* … */ },
// Regras de validacao do formulario
// Atributos / Dados do Formulario e regras de validacao
// Atributos definidos aqui sao copiados internamente para o Objeto formData
formRules: {
emailAddress: {isEmail: true, required: true},
fullName: {required: true},
password: {required: true},
confirmPassword: {required: true, sameAs: 'password'},
},
// Armazena os error retornados pelo servidor, se
// e somente se, a requisicao ao servidor teve
// como origem a API Cloud.js (veja arquivo assets/cloud.setup.js)
// o Cloud insere neste campo somente a mensagem de erro, mas o JSON
// de resposta tem muito mais informacoes e pode ser aproveitado pelo
// programador. Vide `tratamento de erros Cloud.js` mais abaixo.
cloudError: '',
// Flag de sucesso da requisicao enviada.
// Deve ser convertida por acao programada
// dentro metodo invocado pelo evento 'submitted' [@submitted="submittedForm()"]
// invocado pelo <ajax-form> para caso de sucesso.
cloudSuccess: false,
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function() {
//…
},
mounted: async function() {
//…
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
// Este metodo e um exemplo de metodo invocado pelo evento submitted
// do componente <ajax-form>
submittedForm: async function() {
// Tudo aqui sera processado somente se a requisicao enviada
// obtiver 200 OK como resposta.
this.cloudSuccess = true;
},
// Cinco formas possiveis de submeter (submit) os dados para o backend:
// 1) Com AJAX, informando diretamente o nome do dicionario registrado no arquivo cloud.setup.js. Ex.: 'updateBillingCard'
// <ajax-form action='updateBillingCard'...></ajax-form> - os parametros (name) sao passados pelo Core Sails ao Cloud.js
// 2) Com AJAX, delegando a requisicao para um Handler que acionara o Cloud.js indiretamente
// <ajax-form :handle-submitting="handleSubmittingUpdateBillingCard"...> </ajax-form>
// , passando-se os parametros esperados pelo metodo invocado. Ex.:
handleSubmittingUpdateBillingCard: async function(argins) {
var newPaymentSource = argins.newPaymentSource;
await Cloud.updateBillingCard.with(newPaymentSource);
},
// 3) Diretamente com a tag html <form>. Ex. <form action='api/v1/account/update-billing-card'>... </form>,
// mas neste caso e preciso informar o caminho exato da url requisitada e nao sera possivel capturar os erros,
// se houver. Esta opcao, embora mais simples, inviabiliza o uso do recurso AJAX.
// Nos casos acima, sempre sera necessario adicionar ao formulario o CSRF (<input type='hidden' name='_csrf' value='<%=_csrf>').
// Para desativar esta exigencia, modifique o parametro csrf do arquivo 'config/security.js' para false
// 4) Sem o uso de Formulários html, capturando os dados com o Vue e enviando com o recurso Cloud. Esta opção
// simplifica o envio de dados e permite a captura de erros, mas tem a desvantagem de delegar o controle de
// erros ao Handler, exemplos:
// -- no arquivo *.ejs:
<input class=`form-control` v-model=`newPaymentSource`> //observe que nao existe tag <form>, nem atributo `id`, nem `name`
// -- no arquivo *.page.js (Vue 2):
data: { newPaymentSource = `` },
// ...
handleSubmittingUpdateBillingCard: function() {
var newPaymentSource = this.newPaymentSource;
if(newPaymentSource&&newPaymentSource.lenght > 3)
Cloud.updateBillingCard
.with(newPaymentSource)
.then(result=>alert(`deu certo`))
.catch(err=>{
// Vide `tratamento de erros Cloud.js` mais abaixo.
alert(`deu errado`)
})
.finally(alert(`Haja o que houver`))
},
// 5) Utilizando AJAX programaticamente, exemplo:
// $.post( "api/v1/account/update-billing-card", function( data ) {
// $( ".result" ).html( data );
// }).done(function() {
// alert( "second success" );
// })
// .fail(function() {
// alert( "error" );
// })
// .always(function() {
// alert( "finished" );
// });
}
});
// _____ ____ _ _____ _ _ _ ____ ___
// |_ _| _ \ / \|_ _|/ \ | \ | | _ \ / _ \
// | | | |_) | / _ \ | | / _ \ | \| | | | | | | |
// | | | _ < / ___ \| |/ ___ \| |\ | |_| | |_| |
// |_|_|_|_\_\/_/___\_\_/_/ __\_\_| \_|____/ \___/_ _ _ ____ _ ____
// | ____| _ \| _ \ / _ \/ ___| / ___| | / _ \| | | | _ \ | / ___|
// | _| | |_) | |_) | | | \___ \ | | | | | | | | | | | | | |_ | \___ \
// | |___| _ <| _ <| |_| |___) | | |___| |__| |_| | |_| | |_| | |_| |___) |
// |_____|_| \_\_| \_\\___/|____/ \____|_____\___/ \___/|____(_)___/|____/
/*
Cloud.js vem incorporado ao Sails.js como solucao para automacao de
requisicoes HTTP. Em resumo, a API consulta o arquivo de definicao
`cloud.setup.js` para identificar as rotas (paths) associadas a um
determinado nome de rota Cloud. Uma vez identificado, a API faz a
requisicao e controla os erros de resposta. No exemplo abaixo foi
utilizada a abordagem identificada acima como 4) Uso do Vue 2 para
enviar a requisição HTTP ao servidor.
*/
// arquivo save-guideline.js
module.exports = {
friendlyName: 'Salvar guideline',
description: 'Uma action 2 para salvar objetos Guideline',
inputs: {
id: {type: `string`},
sequence: {type: `number`, required: true},
text: { type: `string`, required: true, description: `free text`}
},
exits: {
success: {
description: `Done`
},
// Esta funcao action 2 retorna um erro 402 e uma
// descricao da mensagem de erro ao requisitante http
sequenceAlreadyInUse: {
statusCode: 409,
description: 'O numero de sequencia ja esta em uso!',
},
},
fn: async function (inputs) {
if(inputs.id){
const guideline = await Guideline.updateOne(
{id: inputs.id},
{
sequence: inputs.sequence,
text: inputs.text
}
).intercept('E_UNIQUE', 'sequenceAlreadyInUse')
// este metodo `intercept`, intercepta os erros de banco de dados
// e redireciona para a funcao exit `sequenceAlreadyInUse` definida
// acima.
return {guideline}
}else{
const guideline = await Guideline.create(
{
sequence: inputs.sequence,
text: inputs.text
}
).intercept('E_UNIQUE', 'sequenceAlreadyInUse')
// este metodo `intercept`, intercepta os erros de banco de dados
// e redireciona para a funcao exit `sequenceAlreadyInUse` definida
// acima.
return guideline
}
}
}
/*
Este é o JSON de resposta enviado pelo backend sails (controllers/save-guideline.js)
quando o erro for interceptado. Observe atentamente o atributo "x-exit-description"
do dicionario! ==> "O numero de sequencia ja esta em uso!",
*/
{
"name": "CloudError",
"responseInfo": {
"body": "Conflict",
"statusCode": 409,
"headers": {
"cache-control": "no-cache, no-store",
"connection": "keep-alive",
"content-length": "8",
"content-type": "text/plain; charset=utf-8",
"date": "Sat, 16 Dec 2023 154843 GMT",
"etag": "W/\"8-OfewgPiFJ3o3XA5wgKRYk2ZHNlU\"",
"keep-alive": "timeout=5",
"x-exit": "sequenceAlreadyInUse",
"x-exit-description": "O numero de sequencia ja esta em uso!",
"x-powered-by": "Sails <sailsjs.com>"
},
"data": "Conflict",
"exit": "sequenceAlreadyInUse",
"code": "sequenceAlreadyInUse"
},
"exit": "sequenceAlreadyInUse",
"code": "sequenceAlreadyInUse"
}
/*
Este erro sera recebido pelo componente Vue 2 que fez a requisicao e
sera tratado para resposta na View do Operador.
Observe que aqui capturamos o atributo o atributo "x-exit-description",
mas poderiamos ter feito de uso de quaiquer outros disponiveis no JSON
de resposta.
*/
parasails.registerPage('edit-guideline', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
message: {severity: ``, summary: ``, details: ``},
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function () {
//…
},
mounted: async function () {
//…
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
save: function () {
Cloud
.saveGuideline
.with(this.guideline)
.then(() => {
this.message.severity = `success`
this.message.summary = `Salvo com sucesso`
this.message.details = ``
})
.catch(
err => {
console.log(JSON.stringify(err))
this.message.severity = `error`
this.message.summary = `Erro ao Salvar`
this.message.details = err.responseInfo.headers['x-exit-description']
}
)
},
}
})
// ____ ____ _ _ _____ ____ _ _ _ _____ ____
// / ___| / ___| | | | ____| _ \| | | | | | ____| _ \
// \___ \| | | |_| | _| | | | | | | | | | _| | | | |
// ___) | |___| _ | |___| |_| | |_| | |___| |___| |_| |
// |____/ \____|_| |_|_____|____/ \___/|_____|_____|____/
//
// _ _ ___ _____ ___ _____ ___ ____ _ _____ ___ ___ _ _
// | \ | |/ _ \_ _|_ _| ___|_ _/ ___| / \|_ _|_ _/ _ \| \ | |
// | \| | | | || | | || |_ | | | / _ \ | | | | | | | \| |
// | |\ | |_| || | | || _| | | |___ / ___ \| | | | |_| | |\ |
// |_| \_|\___/ |_| |___|_| |___\____/_/ \_\_| |___\___/|_| \_|
module.exports = {
friendlyName: 'Send Broadcast To Session',
description: 'Envia uma mensagem para a sessao do usuario, no momento agendado.',
extendedDescription: `
Uma simplificacao aplicada da biblioteca
(Node Schedule)[https://www.npmjs.com/package/node-schedule] em
conjunto com os recursos de WebSocket nativos do
(Sails.js WebSocket)[https://sailsjs.com/documentation/reference/web-sockets].
Para saber mais, visite o respectivo sitio indicado acima`,
inputs: {
roomName: { type: `string`, description: `Nome da Sala escolhida para o broadcast`},
sessionName: {type: `string`, description: `Nome da sessao alvo do broadcast`},
cron: {
type: 'string',
description: `A cronologia no padrao Linux Cron a ser aplicada no broadcast`,
extendedDescription: `
* * * * * *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ │
│ │ │ │ │ └ dia da semana (0 - 7) (0 ou 7 para Domingo)
│ │ │ │ └───── numero do mes (1 - 12)
│ │ │ └────────── dia do mes (1 - 31)
│ │ └─────────────── hora (0 - 23)
│ └──────────────────── minuto (0 - 59)
└───────────────────────── segundo (0 - 59, opcional)
`
},
notificationText: {
type: 'string',
description: `Texto a ser visualizado na notificacao por push`
}
},
exits: {
success: {
description: 'Executado.',
},
},
fn: async function (inputs) {
const schedule = require('node-schedule');
const job = schedule.scheduleJob(inputs.cron, () => {
console.log(inputs.notificationText);
sails.sockets.broadcast(inputs.roomName, inputs.sessionName, {notificationText: inputs.notificationText});
// sera recebido pelo cliente WebSocket que esta ouvindo o Socket de nome [roomName] e sessao [sessionName]
// ex.: io.socket.on(`sessionName`, ()=>{...})
});
}
};
// _____ ___ _ _____
// | ___|_ _| | | ____|
// | |_ | || | | _|
// | _| | || |___| |___
// |_|___|___|_____|_____| ____ ___ _ _ _____ ____
// / ___/ _ \| \ | |_ _| _ \ / _ \| | | | | ____| _ \
// | | | | | | \| | | | | |_) | | | | | | | | _| | |_) |
// | |__| |_| | |\ | | | | _ <| |_| | |___| |___| |___| _ <
// \____\___/|_| \_| |_| |_| \_\\___/|_____|_____|_____|_| \_\
/**
* FileController
*
* @description :: logica backend (Server-side) para controle de arquivos
* @help :: See http://links.sailsjs.org/docs/controllers
*/
module.exports = {
/**
* `FileController.upload()`
*
* Envia arquivos para o servidor armazenar em disco local.
*/
upload: function (req, res) {
// e.g.
// 0 => infinito
// 240000 => 4 minutos (240,000 milisegundos)
// etc.
//
// O padrao e de 2 minutos.
res.setTimeout(0);
req.file('nome-do-arquivo')
.upload({
// Devemos definir o tamanho maximo dos arquivos (em bytes)
maxBytes: 1000000
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.allParams()
});
});
},
/**
* `FileController.s3upload()`
*
* Envia arquivos para o servidor armazenar em no AWS S3 (Buckets).
*
* NOTA:
* Se o arquivo a ser enviado e realmente grande, considere aumentar o
* timeout da conexao TCP no servidor.
*/
s3upload: function (req, res) {
// e.g.
// 0 => infinito
// 240000 => 4 minutos (240,000 milisegundos)
// etc.
//
// O padrao e de 2 minutos.
res.setTimeout(0);
req.file('avatar').upload({
adapter: require('skipper-s3'),
bucket: process.env.BUCKET,
key: process.env.KEY,
secret: process.env.SECRET
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.allParams()
});
});
},
/**
* `FileController.gridFS()`
*
* Envia arquivos para o servidor armazenar no MongoDB GridFS.
*
* NOTA:
* Se o arquivo a ser enviado e realmente grande, considere aumentar o
* timeout da conexao TCP no servidor.
*/
gridFS: function (req, res) {
// e.g.
// 0 => infinito
// 240000 => 4 minutos (240,000 milisegundos)
// etc.
//
// O padrao e de 2 minutos.
res.setTimeout(0);
req.file('avatar').upload({
adapter: require('skipper-gridfs'),
uri: 'mongodb://[username:password@]host1[:port1][/[database[.bucket]]'
}, function whenDone(err, uploadedFiles) {
if (err) return res.serverError(err);
else return res.json({
files: uploadedFiles,
textParams: req.allParams()
});
},
/**
* FileController.download()
*
* Recebe arquivos do servidor, obtidos do armazenamento local.
*/
download: function (req, res) {
var Path = require('path');
var fs = require('fs');
// If a relative path was provided, resolve it relative
// to the cwd (which is the top-level path of this sails app)
fs.createReadStream(Path.resolve(req.param('path')))
.on('error', function (err) {
return res.serverError(err);
})
.pipe(res);
}
};
// __ __
// | \/ | ___ _ __ ___ __ _ __ _ ___ _ __ ___
// | |\/| |/ _ \ '_ \/ __|/ _` |/ _` |/ _ \ '_ ` _ \
// | | | | __/ | | \__ \ (_| | (_| | __/ | | | | |
// |_| |_|\___|_| |_|___/\__,_|\__, |\___|_| |_| |_|
// __ __ _ _ |___/
// | \/ | ___ __| | __ _| |
// | |\/| |/ _ \ / _` |/ _` | |
// | | | | (_) | (_| | (_| | |
// |_| |_|\___/ \__,_|\__,_|_|
/**
* <message>
* -----------------------------------------------------------------------------
* A modal dialog pop-up.
*
* > Be careful adding other Vue.js lifecycle callbacks in this file! The
* > finnicky combination of Vue transitions and bootstrap modal animations used
* > herein work, and are very well-tested in practical applications. But any
* > changes to that specific cocktail could be unpredictable, with unsavory
* > consequences.
*
* @type {Component}
*
*
* -----------------------------------------------------------------------------
*/
parasails.registerComponent('message', {
// ╔═╗╦═╗╔═╗╔═╗╔═╗
// ╠═╝╠╦╝║ ║╠═╝╚═╗
// ╩ ╩╚═╚═╝╩ ╚═╝
props: [
'severity',
'summary',
'details'
],
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: function () {
return {}
},
// ╦ ╦╔╦╗╔╦╗╦
// ╠═╣ ║ ║║║║
// ╩ ╩ ╩ ╩ ╩╩═╝
template: `
<!-- use:
<message id="msg" v-on:close="cleanMessage()" :severity="message.severity" :summary="message.summary" :details="message.details"></message>
it shows message according given severity (error|warn|success|info) if summary lenght is greater than 0. These details are optional
to clean message just clean message.summary value under cleanMessage() method.
-->
<div>
<div v-if="show()">
<div class="modal fade show" tabindex="-1" style="display: block; overflow: visible; box-shadow: #5a5a5a">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<div v-if="severity=='success'">
<span class="fw-bolder text-success"><em class="fa fa-check"></em> {{summary}}</span>
</div>
<div v-if="severity=='info'">
<span class="fw-bolder text-info"><em class="fa fa-check"></em> {{summary}}</span>
</div>
<div v-if="severity=='warn'">
<span class="fw-bolder text-warning"><em class="fa fa-warning"></em> {{summary}}</span>
</div>
<div v-if="severity=='error'">
<span class="fw-bolder text-danger"><em class="fa fa-bug"></em> {{summary}}</span>
</div>
</h5>
<button type="button" class="btn-close" @click="$emit('close', '')" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="text-muted">{{details}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @click="$emit('close', '')"><em class="fa fa-close"></em></button>
</div>
</div>
</div>
</div>
</div>
</div>
`,
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function () {
},
mounted: function () {
},
// ^Note that there is no `beforeDestroy()` lifecycle callback in this
// component. This is on purpose, since the timing vs. `leave()` gets tricky.
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
show: function () {
return this.summary !== ''
}
}
})
// __ _______ ____ ____ ___ ___ _ _ ___ _ _ ____
// \ \ / / ____| _ \/ ___|_ _/ _ \| \ | |_ _| \ | |/ ___|
// \ \ / /| _| | |_) \___ \| | | | | \| || || \| | | _
// \ V / | |___| _ < ___) | | |_| | |\ || || |\ | |_| |
// \_/ |_____|_| \_\____/___\___/|_| \_|___|_| \_|\____|
module.exports = {
friendlyName: 'Versioning',
description: 'Automatiza o controle de versoes.',
extendedDescription: `
Este script utiliza o arquivo ./VERSIONING.md para registro do historico de versoes
de seu Prototipo Sails.js.
Para que o Sails seja capaz de identificar corretamente a raiz do Prototipo, se faz
necessario executa-lo durante a inicializacao do App (sails lift ou npm start). Por
esse motivo requer que seja adicionado ao arquivo 'config/boostrap.js' uma linha de
chamada da funcao 'fn' como se segue:
require('/scripts/versioning.js').fn() // adicionar no inicio do corpo do modulo
O Script compara a versao do Prototipo registrada no arquivo 'package.json' com a
lista de versoes registradas no VERSIONING.md, se nao encontrar,obtem do dicionario
os atributos version e description para criar um registro do tipo:
version: Mon Nov 13 2023 10:38:37 GMT-0300 (Horário Padrão de Brasília)- description
Portanto, para manter seu historico de versoes atualizado, basta alterar os valores
dos atributos "version" e "description" a cada nova "release" lancada.
`,
fn: async function () {
sails.log('Executando shell script customizado... (`sails run versioning`)');
const path = require('path');
const fs = require('fs');
const infoPath = path.resolve(sails.config.appPath, 'package.json');
const info = JSON.parse(fs.readFileSync(infoPath).toString());
const fileName = path.resolve(sails.config.appPath, 'VERSIONING.md');
fs.open(fileName, (err) => {
if (err) {
fs.writeFileSync(fileName, `v.0.0.0: ${new Date} - Start` + '\n');
} else {
const file = fs.readFileSync(fileName).toString();
if (file.indexOf(`v.${info.version}`) !== -1) {
console.log('Skipped');
return;
}
fs.appendFile(fileName, `v.${info.version}: ${new Date()} - ${info.description}` + '\n', (err) => {
if (err) {
console.error(err);
} else {
console.log('Recorded');
}
});
}
});
sails.log('Finished custom shell script... (`sails run versioning`)');
}
};
# Para instalar a imagem mais atualizada...
# FROM registry.ccarj.intraer/mirror/library/node:lts-alpine AS build
#
# Para instalar a imagem compativel com a versao de 'geracao' do app
# FROM node:16.15.1 AS build
FROM node:16.15.1 AS build
# Definicao do diretorio de trabalho
WORKDIR /usr/local/app
# Transferencia do codigo fonte para a imagem
COPY ./ /usr/local/app/
# Instalacao de todas as dependencias
RUN npm install
# Atribuicao de permissoes de execucao
USER node
COPY . .
COPY --chown=node:node . .
# Exposicao da porta 80
EXPOSE 80:80
# Execucao em ambiente de producao
ENV NODE_ENV=production
CMD [ "node", "./app.js" ]