aloux-iam 0.0.140 → 0.0.142

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,79 @@
1
- const self = module.exports
1
+ const fs = require("fs");
2
+ const self = module.exports;
2
3
 
3
4
  self.responseError = async (res, error) => {
4
- let obj = error
5
- if (!error.code) {
6
- obj = {
7
- code: 400,
8
- title: 'Error',
9
- detail: error.message,
10
- suggestion: 'Revisar el detalle'
11
- }
12
- }
13
- res.status(obj.code).send(obj)
14
- }
5
+ let obj = error;
6
+ if (!error.code) {
7
+ obj = {
8
+ code: 400,
9
+ title: "Error",
10
+ detail: error.message,
11
+ suggestion: "Revisar el detalle",
12
+ };
13
+ }
14
+ res.status(obj.code).send(obj);
15
+ };
15
16
 
16
17
  self.generatePaginationResponse = async (count, page, itemsPerPage, items) => {
17
- const totalPages = Math.ceil(count / itemsPerPage)
18
- const currentPage = Math.max(1, Math.min(Number(page), totalPages))
19
- const finalCurrentPage = totalPages === 0 ? 1 : currentPage
20
- const remainingPages = Math.max(0, totalPages - finalCurrentPage)
21
- return { currentPage: finalCurrentPage, totalPages, perPage: Number(itemsPerPage), count, remainingPages, items }
22
- }
18
+ const totalPages = Math.ceil(count / itemsPerPage);
19
+ const currentPage = Math.max(1, Math.min(Number(page), totalPages));
20
+ const finalCurrentPage = totalPages === 0 ? 1 : currentPage;
21
+ const remainingPages = Math.max(0, totalPages - finalCurrentPage);
22
+ return { currentPage: finalCurrentPage, totalPages, perPage: Number(itemsPerPage), count, remainingPages, items };
23
+ };
24
+
25
+ self.brand = {
26
+ _cfg: null,
27
+
28
+ init(config) {
29
+ this._cfg = config;
30
+ },
31
+
32
+ _get(key) {
33
+ if (this._cfg) return this._cfg[key];
34
+ const APP = process.env.APP;
35
+ if (APP) return process.env[`${key}_${APP}`] || process.env[key];
36
+ return process.env[key];
37
+ },
38
+
39
+ name() {
40
+ return this._get("PROJECT_NAME");
41
+ },
42
+
43
+ template(key) {
44
+ const path = this._get(key);
45
+ if (!path) {
46
+ throw {
47
+ code: 500,
48
+ title: "Error de configuración",
49
+ detail: `Template no encontrado: ${key}`,
50
+ suggestion: "Verifica que la variable de entorno esté definida",
51
+ error: new Error(),
52
+ };
53
+ }
54
+ try {
55
+ let file = fs.readFileSync(path, "utf8");
56
+ file = file.replace(/\+\+\+brandName\+\+\+/g, this.name() || "");
57
+ file = file.replace(/\+\+\+brandColor\+\+\+/g, this._get("BRAND_COLOR") || "");
58
+ file = file.replace(/\+\+\+brandLogo\+\+\+/g, this._get("BRAND_LOGO") || "");
59
+ return file;
60
+ } catch (e) {
61
+ throw {
62
+ code: 500,
63
+ title: "Error al leer template",
64
+ detail: `No se pudo leer el archivo: ${path}`,
65
+ suggestion: "Verifica que el path del template sea correcto",
66
+ error: e,
67
+ };
68
+ }
69
+ },
70
+ };
71
+
72
+ // Gkey
73
+ self.resolveGkey = (business) => {
74
+ const businessGkey = business.gkey || null;
75
+ const hasOwnKey = businessGkey?.status === true;
76
+
77
+ if (hasOwnKey) return { gkey: businessGkey, source: "business" };
78
+ return { gkey: null, source: null };
79
+ };
@@ -1,7 +1,9 @@
1
1
  const Business = require("../models/Business");
2
+ const Company = require("../models/Company");
2
3
  const utils = require("../config/utils");
3
4
  const AlouxAWS = require("./operationsAWS");
4
5
  const errorController = require("../config/utils");
6
+ const { resolveGkey } = require("../config/utils");
5
7
 
6
8
  const self = module.exports;
7
9
 
@@ -11,16 +13,26 @@ self.create = async (req, res) => {
11
13
  req.body.lastUpdate = req.body.createdAt;
12
14
  req.body.status = "Activo";
13
15
 
16
+ let inheritedGkey = null;
17
+ if (req.body.data?.useCompanyKey && req.body._company) {
18
+ const company = await Company.findOne({ _id: req.body._company }).lean();
19
+ const companyGkey = company?.data?.gkey;
20
+ if (companyGkey?.status) inheritedGkey = companyGkey;
21
+ }
22
+
14
23
  if (req.body.environment && req.body.environment.length > 1) {
15
24
  for (let i in req.body.environment) {
16
25
  const business = new Business(req.body);
17
26
  business.environment = req.body.environment[i];
27
+ if (inheritedGkey) business.gkey = inheritedGkey;
18
28
  await business.save();
19
29
  }
20
30
  } else {
21
31
  const business = new Business(req.body);
32
+ if (inheritedGkey) business.gkey = inheritedGkey;
22
33
  await business.save();
23
34
  }
35
+
24
36
  await res.status(201).send({});
25
37
  } catch (error) {
26
38
  await errorController.responseError(res, error);
@@ -250,3 +262,56 @@ self.identity = async (req, res) => {
250
262
  await errorController.responseError(res, error);
251
263
  }
252
264
  };
265
+
266
+ self.setUseCompanyKey = async (req, res) => {
267
+ try {
268
+ const business = await Business.findOne({ _id: req.params.BUSINESS_ID }).lean();
269
+ if (!business) throw { code: 404, title: "No encontrado", detail: "No existe el negocio" };
270
+
271
+ await Business.updateOne(
272
+ { _id: req.params.BUSINESS_ID },
273
+ {
274
+ $unset: { gkey: "" },
275
+ $set: {
276
+ "data.useCompanyKey": false,
277
+ lastUpdate: new Date().getTime(),
278
+ },
279
+ }
280
+ );
281
+
282
+ res.status(202).send({ ok: true });
283
+ } catch (error) {
284
+ await errorController.responseError(res, error);
285
+ }
286
+ };
287
+
288
+ self.inheritKey = async (req, res) => {
289
+ try {
290
+ const business = await Business.findOne({ _id: req.params.BUSINESS_ID })
291
+ .populate("_company")
292
+ .lean();
293
+ if (!business) throw { code: 404, title: "No encontrado", detail: "No existe el negocio" };
294
+
295
+ const companyGkey = business._company?.data?.gkey;
296
+ if (!companyGkey?.status) throw {
297
+ code: 400,
298
+ title: "Sin llave",
299
+ detail: "La organización no tiene una llave de Google Cloud configurada",
300
+ };
301
+
302
+ await Business.updateOne(
303
+ { _id: req.params.BUSINESS_ID },
304
+ {
305
+ $set: {
306
+ gkey: companyGkey,
307
+ "data.useCompanyKey": true,
308
+ lastUpdate: new Date().getTime(),
309
+ },
310
+ }
311
+ );
312
+
313
+ res.status(202).send({ ok: true });
314
+ } catch (error) {
315
+ await errorController.responseError(res, error);
316
+ }
317
+ };
@@ -153,3 +153,38 @@ self.identity = async (req, res) => {
153
153
  await errorController.responseError(res, error);
154
154
  }
155
155
  };
156
+
157
+ self.updateGkey = async (req, res) => {
158
+ try {
159
+ const company = await Company.findOne({ _id: req.params.COMPANY_ID });
160
+ if (!company) throw { code: 404, title: "No encontrado", detail: "No existe la organización" };
161
+
162
+ company.data = { ...(company.data || {}), gkey: req.body.gkey };
163
+ company.lastUpdate = new Date().getTime();
164
+ company.markModified("data");
165
+ await company.save();
166
+
167
+ res.status(202).send({ ok: true });
168
+ } catch (error) {
169
+ await errorController.responseError(res, error);
170
+ }
171
+ };
172
+
173
+ self.deleteGkey = async (req, res) => {
174
+ try {
175
+ const company = await Company.findOne({ _id: req.params.COMPANY_ID });
176
+ if (!company) throw { code: 404, title: "No encontrado", detail: "No existe la organización" };
177
+
178
+ const newData = { ...(company.data || {}) };
179
+ delete newData.gkey;
180
+
181
+ company.data = newData;
182
+ company.lastUpdate = new Date().getTime();
183
+ company.markModified("data");
184
+ await company.save();
185
+
186
+ res.status(200).send({ ok: true });
187
+ } catch (error) {
188
+ await errorController.responseError(res, error);
189
+ }
190
+ };
@@ -0,0 +1,13 @@
1
+ const passwordService = require("../services/generatePassword");
2
+
3
+ const self = module.exports;
4
+
5
+ self.generate = (req, res) => {
6
+ try {
7
+ const length = Number(req.query.length) || 12;
8
+ const password = passwordService.generatePassword(length);
9
+ return res.json({ password });
10
+ } catch (error) {
11
+ return res.status(500).json({ message: "Error al generar la contraseña", error });
12
+ }
13
+ };
@@ -1,5 +1,6 @@
1
1
  const Log = require("../models/Log");
2
2
  const Label = require("../models/Label");
3
+ const mongoose = require("mongoose");
3
4
  const self = module.exports;
4
5
 
5
6
  self.create = async (req, res) => {
@@ -8,9 +9,11 @@ self.create = async (req, res) => {
8
9
  req.header("Company") !== "undefined" ? req.header("Company") : null;
9
10
 
10
11
  const log = new Log(req.body);
12
+ const businessId = req.header("business") !== "undefined" ? req.header("business") : null;
11
13
  log.createdAt = new Date().getTime();
12
14
  log._user = req.user._id;
13
15
  log._company = companyId;
16
+ log._business = businessId;
14
17
 
15
18
  log.label = req.body.label;
16
19
  await log.save();
@@ -33,6 +36,7 @@ self.update = async (req, resp) => {
33
36
  resp.status(400).send({ error: error.message });
34
37
  }
35
38
  };
39
+
36
40
  self.status = async (req, resp) => {
37
41
  try {
38
42
  const _id = req.params.LOG_ID;
@@ -46,43 +50,44 @@ self.status = async (req, resp) => {
46
50
  resp.status(400).send({ error: error.message });
47
51
  }
48
52
  };
53
+
49
54
  self.retrieve = async (req, res) => {
50
55
  try {
51
56
  const companyId =
52
57
  req.header("Company") !== "undefined" ? req.header("Company") : null;
53
- let query = { _company: companyId };
54
58
 
55
- if (req.body.users.length) {
56
- query = {
57
- _user: { $in: req.body.users },
59
+ const query = { _company: companyId };
60
+
61
+ if (req.body.users?.length) {
62
+ query._user = {
63
+ $in: req.body.users.map((id) => new mongoose.Types.ObjectId(id)),
58
64
  };
59
65
  }
60
66
 
61
- console.log(new Date())
62
- const consulta = await Log.find(query)
63
- .populate([{ path: "_user", select: { name: 1, lastName: 1 } }])
64
- .sort({ createdAt: 1 })
65
- .lean();
66
- console.log(new Date())
67
+ if (req.body.dateStart || req.body.dateEnd) {
68
+ query.createdAt = {};
69
+ if (req.body.dateStart) query.createdAt.$gte = Number(req.body.dateStart);
70
+ if (req.body.dateEnd) query.createdAt.$lte = Number(req.body.dateEnd);
71
+ }
67
72
 
68
- // for (let i in consulta) {
69
- // consulta[i].label = consulta[i]._label.label;
70
- // await consulta[i].save();
71
- // }
73
+ if (req.body.business?.length) {
74
+ query._business = { $in: req.body.business };
75
+ }
76
+
77
+ const consulta = await Log.find(query).populate("_user", "name lastName email");
72
78
 
73
79
  const response = {
74
80
  dataset0: { field: "Visualizaciones totales", count: consulta.length },
75
- dataset1: [],
81
+ dataset1: processDataset1(consulta),
76
82
  dataset2: processDataset2(consulta),
77
83
  dataset3: processDataset3(consulta),
78
84
  dataset4: processDataset4(consulta),
79
- dataset5: [],
85
+ dataset5: processDataset5(consulta),
80
86
  dataset6: processDataset6(consulta),
81
87
  dataset7: processDataset7(consulta),
88
+ dataset8: processDataset8(consulta, req.body.search, req.body.page, req.body.limit),
82
89
  };
83
90
 
84
- console.log(new Date())
85
-
86
91
  res.status(200).send(response);
87
92
  } catch (error) {
88
93
  console.error(error);
@@ -90,35 +95,23 @@ self.retrieve = async (req, res) => {
90
95
  }
91
96
  };
92
97
 
93
- function formatDate(date) {
94
- const day = String(date.getDate()).padStart(2, "0");
98
+ function formatDate(isoDate) {
99
+ const [year, month, day] = isoDate.split("-");
95
100
  const monthNames = [
96
- "Ene",
97
- "Feb",
98
- "Mar",
99
- "Abr",
100
- "May",
101
- "Jun",
102
- "Jul",
103
- "Ago",
104
- "Sep",
105
- "Oct",
106
- "Nov",
107
- "Dic",
101
+ "Ene", "Feb", "Mar", "Abr", "May", "Jun",
102
+ "Jul", "Ago", "Sep", "Oct", "Nov", "Dic",
108
103
  ];
109
- const month = monthNames[date.getMonth()];
110
- const year = date.getFullYear();
111
- return `${day} ${month} ${year}`;
104
+ return `${day} ${monthNames[parseInt(month) - 1]} ${year}`;
112
105
  }
106
+
113
107
  function processDataset1(consulta) {
114
- return consulta.map((item) => {
115
- return {
116
- _id: item._id,
117
- labelDescription: item.label,
118
- userName: item._user.name + " " + item._user.lastName,
119
- createdAt: item.createdAt,
120
- };
121
- });
108
+ return consulta.map((item) => ({
109
+ _id: item._id,
110
+ labelDescription: item.label,
111
+ userName: item._user.name + " " + item._user.lastName,
112
+ userEmail: item._user.email,
113
+ createdAt: item.createdAt,
114
+ }));
122
115
  }
123
116
 
124
117
  function processDataset2(consulta) {
@@ -153,7 +146,8 @@ function processDataset3(consulta) {
153
146
 
154
147
  function processDataset4(consulta) {
155
148
  const dateCounts = consulta.reduce((acc, item) => {
156
- const date = formatDate(new Date(item.createdAt));
149
+ const isoDate = new Date(item.createdAt).toISOString().split("T")[0];
150
+ const date = formatDate(isoDate);
157
151
  acc[date] = (acc[date] || 0) + 1;
158
152
  return acc;
159
153
  }, {});
@@ -170,7 +164,8 @@ function processDataset5(consulta) {
170
164
  const categories = new Set();
171
165
 
172
166
  consulta.forEach((item) => {
173
- const date = formatDate(new Date(item.createdAt));
167
+ const isoDate = new Date(item.createdAt).toISOString().split("T")[0];
168
+ const date = formatDate(isoDate);
174
169
  const label = item.label;
175
170
  categories.add(date);
176
171
 
@@ -200,55 +195,80 @@ function processDataset5(consulta) {
200
195
  function processDataset6(data) {
201
196
  const userActivity = {};
202
197
 
203
- // Count activity per user. Assuming data contains _user with name and lastName
204
198
  data.forEach((item) => {
205
199
  const fullName = `${item._user.name} ${item._user.lastName}`;
206
200
  userActivity[fullName] = (userActivity[fullName] || 0) + 1;
207
201
  });
208
202
 
209
- // Sort users by activity count in descending order
210
- const sortedUsers = Object.entries(userActivity).sort(
211
- ([, countA], [, countB]) => countB - countA
212
- );
203
+ const topUsers = Object.entries(userActivity)
204
+ .sort(([, a], [, b]) => b - a)
205
+ .slice(0, 10);
213
206
 
214
- // Extract top 10 users
215
- const topUsers = sortedUsers.slice(0, 10);
216
-
217
- // Build the result object
218
- const result = {
207
+ return {
208
+ field: "Usuarios con mas actividad en la plataforma",
219
209
  counts: topUsers.map(([, count]) => count),
220
210
  actionsName: topUsers.map(([fullName]) => fullName.split(" ")),
221
- field: "Usuarios con mas actividad en la plataforma",
222
211
  };
223
-
224
- return result;
225
212
  }
226
213
 
227
214
  function processDataset7(data) {
228
215
  const userActivity = {};
229
216
 
230
- // Count activity per user. Assuming data contains _user with name and lastName
231
217
  data.forEach((item) => {
232
218
  const fullName = `${item._user.name} ${item._user.lastName}`;
233
219
  userActivity[fullName] = (userActivity[fullName] || 0) + 1;
234
220
  });
235
221
 
236
- // Sort users by activity count in ascending order
237
- const sortedUsers = Object.entries(userActivity).sort(
238
- ([, countA], [, countB]) => countA - countB
239
- );
240
-
241
- // Extract top 10 least active users
242
- const leastActiveUsers = sortedUsers.slice(0, 10);
222
+ const leastUsers = Object.entries(userActivity)
223
+ .sort(([, a], [, b]) => a - b)
224
+ .slice(0, 10);
243
225
 
244
- // Build the result object
245
- const result = {
246
- counts: leastActiveUsers.map(([, count]) => count),
247
- actionsName: leastActiveUsers.map(([fullName]) => fullName.split(" ")),
226
+ return {
248
227
  field: "Usuarios con menos actividad en la plataforma",
228
+ counts: leastUsers.map(([, count]) => count),
229
+ actionsName: leastUsers.map(([fullName]) => fullName.split(" ")),
249
230
  };
231
+ }
232
+
233
+ function processDataset8(data, search = "", page = 1, limit = 10) {
234
+ const userActivity = {};
235
+
236
+ data.forEach((item) => {
237
+ const fullName = `${item._user.name} ${item._user.lastName}`;
238
+ if (!userActivity[fullName]) {
239
+ userActivity[fullName] = {
240
+ name: item._user.name,
241
+ lastName: item._user.lastName,
242
+ email: item._user.email,
243
+ count: 0,
244
+ };
245
+ }
246
+ userActivity[fullName].count++;
247
+ });
248
+
249
+ let users = Object.values(userActivity).sort((a, b) => b.count - a.count);
250
+
251
+ if (search) {
252
+ const s = search.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
253
+ users = users.filter((u) => {
254
+ const full = `${u.name} ${u.lastName} ${u.email}`.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
255
+ return full.includes(s);
256
+ });
257
+ }
250
258
 
251
- return result;
259
+ const total = users.length;
260
+ const totalPages = Math.ceil(total / limit);
261
+ const start = (page - 1) * limit;
262
+ const items = users.slice(start, start + limit);
263
+
264
+ return {
265
+ field: "Actividad de usuarios",
266
+ total,
267
+ page,
268
+ limit,
269
+ totalPages,
270
+ items,
271
+ };
252
272
  }
253
273
 
254
274
  self.get = async (req, res) => {
@@ -281,4 +301,4 @@ self.count = async (req, res) => {
281
301
  } catch (error) {
282
302
  res.status(400).send({ error: error.message });
283
303
  }
284
- };
304
+ };
@@ -0,0 +1,46 @@
1
+ const Totp = require("../services/totp");
2
+ const utils = require("../config/utils");
3
+
4
+ const self = module.exports;
5
+
6
+ self.setup = async (req, res) => {
7
+ try {
8
+ const tempToken = req.query.tempToken || req.body?.tempToken;
9
+ const response = await Totp.setup(tempToken);
10
+ res.status(200).send(response);
11
+ } catch (error) {
12
+ await utils.responseError(res, error);
13
+ }
14
+ };
15
+
16
+ self.activate = async (req, res) => {
17
+ try {
18
+ const tempToken = req.headers["x-temp-token"] || req.body?.tempToken;
19
+ const response = await Totp.activate(tempToken, req.body.token);
20
+ res.status(200).send(response);
21
+ } catch (error) {
22
+ await utils.responseError(res, error);
23
+ }
24
+ };
25
+
26
+ self.checkLogin = async (req, res) => {
27
+ try {
28
+ const { token, tempToken } = req.body;
29
+ if (!token || !tempToken)
30
+ return res.status(400).send({ message: "Faltan parámetros" });
31
+
32
+ const response = await Totp.checkLogin(token, tempToken, res);
33
+ res.status(200).send(response);
34
+ } catch (error) {
35
+ await utils.responseError(res, error);
36
+ }
37
+ };
38
+
39
+ self.adminToggle = async (req, res) => {
40
+ try {
41
+ const response = await Totp.adminToggle(req.params.USER_ID, req.body.enabled);
42
+ res.status(200).send(response);
43
+ } catch (error) {
44
+ await utils.responseError(res, error);
45
+ }
46
+ };