Compare commits

...
Author SHA1 Message Date
Rafael 5a36762544 UPDATE: route /catalog/register-dataset will have an limit of 10mb 2025-05-26 10:24:48 -03:00
Marcos Rodrigues Silva f579171024 Merge pull request #302 from dadosfera/beta
Default value to header origin
2025-05-15 11:18:17 -03:00
Marcos Rodrigues Silva 885e5f4568 Merge pull request #301 from dadosfera/fix/remove-default-host
Fix/remove default host
2025-05-15 11:00:45 -03:00
marcos-silva-rodrigues 81d8cd8a07 FIX: default value to header origin 2025-05-15 11:00:07 -03:00
Marcos Rodrigues Silva f153f41357 Merge pull request #300 from dadosfera/beta
Beta
2025-05-14 18:30:54 -03:00
marcos-silva-rodrigues 2b9e3ff3bc Merge branch 'main' into beta 2025-05-13 14:32:34 -03:00
Marcos Rodrigues Silva 17cdaac380 Merge pull request #299 from dadosfera/fix/stg-envs
FIX: stg to prd for sbm environment
2025-05-09 11:07:22 -03:00
marcos-silva-rodrigues e4eb388e18 FIX: stg to prd for sbm environment 2025-05-09 11:06:23 -03:00
Marcos Rodrigues Silva 4bea0816b6 Merge pull request #298 from dadosfera/feature/pii-rules
FIX: install chromiun in base_image
2025-04-29 16:22:22 -03:00
marcos-silva-rodrigues fb491328d6 FIX: install chromiun in base_image 2025-04-29 16:20:37 -03:00
Marcos Rodrigues Silva 4e043d93ce Merge pull request #297 from dadosfera/feature/pii-rules
REFACTOR: move template to assets folder
2025-04-29 15:41:16 -03:00
marcos-silva-rodrigues 0b525b835f REFACTOR: move template to assets folder 2025-04-29 15:40:37 -03:00
Marcos Rodrigues Silva 3c8c39ec26 Merge pull request #296 from dadosfera/feature/pii-rules
REFACTOR: create builder for parser reporter and fix pdf
2025-04-29 15:20:46 -03:00
marcos-silva-rodrigues 36997403e1 REFACTOR: create builder for parser reporterand fix pdf 2025-04-29 15:18:46 -03:00
Marcos Rodrigues Silva 1175591521 Merge pull request #295 from dadosfera/feature/pii-rules
Feature/pii rules
2025-04-29 14:04:06 -03:00
marcos-silva-rodrigues 04accb6689 FIX: correct stg urls 2025-04-29 14:02:24 -03:00
marcos-silva-rodrigues 58e3c7be94 FEAT: pii 2025-04-29 14:00:23 -03:00
marcos-silva-rodrigues 7a6db36554 FEAT: generate pii reporter 2025-04-27 13:00:49 -03:00
Marcos Rodrigues Silva 80ebd98f6f Merge pull request #294 from dadosfera/feature/logs_dashboard
Feature/logs dashboard
2025-04-23 17:46:18 -03:00
Marcos Rodrigues Silva 9e0495fc26 Merge pull request #293 from dadosfera/feat/enforce-mfa
Feat/enforce mfa
2025-04-22 14:50:39 -03:00
marcos-silva-rodrigues f2437a8b3c FIX: add decorator to require danger zone module 2025-04-22 14:46:39 -03:00
marcos-silva-rodrigues 9e87562e23 FEAT: update protospack 2025-04-22 09:43:34 -03:00
marcos-silva-rodrigues 8f5cb13cc3 FEAT: enforce mfa 2025-04-21 18:53:34 -03:00
Marcos Rodrigues Silva e0a0ec9911 Merge pull request #292 from dadosfera/feature/logs_dashboard
Feature/logs dashboard
2025-04-17 18:12:28 -03:00
marcos-silva-rodrigues 419c063c6c FEAT: add access dashboard 2025-04-17 18:04:18 -03:00
marcos-silva-rodrigues 2aed66d371 FEAT: add decorator to block endpoint if not found module 2025-04-17 15:20:27 -03:00
Marcos Rodrigues Silva 1f45436c13 Merge pull request #291 from dadosfera/fix/network-policy
FIX: send origin header
2025-04-16 17:13:55 -03:00
marcos-silva-rodrigues df8520afd3 FIX: send origin header 2025-04-15 14:24:41 -03:00
Marcos Rodrigues Silva 95187e6b80 Merge pull request #290 from dadosfera/debug/unimed-sbm
UPDATE: change in-factory, pi-factory, tr-factory to prod
2025-04-14 14:15:45 -03:00
marcos-silva-rodrigues c8a3ace052 UPDATE: change in-factory, pi-factory, tr-factory to prod 2025-04-14 14:14:46 -03:00
Marcos Rodrigues Silva c059ac45bf Merge pull request #289 from dadosfera/fix/network-policy
FIX: send origin removing http prefix
2025-04-14 08:46:56 -03:00
marcos-silva-rodrigues f4b8addda4 FIX: send origin removing http prefix 2025-04-14 08:45:59 -03:00
Marcos Rodrigues Silva aa3a1cc300 Merge pull request #288 from dadosfera/fix/network-policy
FIX: remove host and send custom host
2025-04-14 08:32:19 -03:00
marcos-silva-rodrigues 0a5f138829 FIX: remove host and send custom host 2025-04-14 08:30:38 -03:00
Marcos Rodrigues Silva 9f427f5873 Merge pull request #287 from dadosfera/fix/network-policy
UPDATE: send host
2025-04-11 18:03:11 -03:00
marcos-silva-rodrigues bacbf42a04 UPDATE: send host 2025-04-11 18:00:51 -03:00
Marcos Rodrigues Silva 56a9d52466 Merge pull request #286 from dadosfera/fix/network-policy
UPDATE: send origin header
2025-04-11 17:37:56 -03:00
marcos-silva-rodrigues 2c964253ec UPDATE: send header in metadata 2025-04-11 17:33:34 -03:00
marcos-silva-rodrigues e886bea05c UPDATE: send origin header 2025-04-11 17:18:45 -03:00
rafael-moraes-ddf 66affed338 Merge pull request #285 from dadosfera/feature/logs_dashboard
Feature/logs dashboard
2025-04-11 16:22:05 -03:00
Rafael Moraes a8ae07e6c4 FEAT: url fix 2025-04-11 16:06:55 -03:00
Rafael Moraes 0df286a5f7 FEAT: att docsfera 2025-04-11 15:23:41 -03:00
Marcos Rodrigues Silva 519a8feb14 Merge pull request #284 from dadosfera/fix/network-policy
UPDATE: send host
2025-04-11 15:08:01 -03:00
marcos-silva-rodrigues 16cb00f46f UPDATE: send host 2025-04-11 15:06:02 -03:00
Marcos Rodrigues Silva 3c59602666 Merge pull request #283 from dadosfera/fix/network-policy
FIX: remove host metadata
2025-04-10 11:55:32 -03:00
marcos-silva-rodrigues 3e0dbc7401 FIX: remove host metadata 2025-04-10 11:53:53 -03:00
Marcos Rodrigues Silva a24d29b56b Merge pull request #282 from dadosfera/fix/network-policy
FIX: send host metadata
2025-04-10 11:47:09 -03:00
marcos-silva-rodrigues deb32b5c0f FIX: send host metadata 2025-04-10 11:44:52 -03:00
Marcos Rodrigues Silva d4a6d2fb4a Merge pull request #281 from dadosfera/fix/network-policy
Fix/network policy
2025-04-10 11:34:00 -03:00
Marcos Rodrigues Silva 01ea0e9561 Merge branch 'beta' into fix/network-policy 2025-04-10 11:33:43 -03:00
marcos-silva-rodrigues 9676b51f01 FIX: comment host metadata 2025-04-10 11:27:07 -03:00
Marcos Rodrigues Silva b0d2f86eaf Merge pull request #280 from dadosfera/feat/network-policy
FEAT: networks get endpoint
2025-04-09 15:34:18 -03:00
marcos-silva-rodrigues f356efb42e Merge branch 'beta' into feat/network-policy 2025-04-09 15:32:10 -03:00
marcos-silva-rodrigues 559d98e884 FIX: merge with beta 2025-04-09 15:28:23 -03:00
Marcos Rodrigues Silva d8623b88df Merge pull request #277 from dadosfera/feat/network-policy
Feat/network policy
2025-04-09 11:36:32 -03:00
Marcos Rodrigues Silva 7044d6606e Merge pull request #278 from dadosfera/beta
Beta
2025-04-08 18:04:41 -03:00
Marcos Rodrigues Silva 92aab57d37 Merge pull request #279 from dadosfera/fix/reset-block-users
FIX: send all metadata
2025-04-08 14:35:10 -03:00
marcos-silva-rodrigues f661055ae8 FIX: send all metadata 2025-04-08 13:49:10 -03:00
Rafael Moraes d310ec2b93 FEAT: logs_dasboard 2025-04-07 11:55:06 -03:00
marcos-silva-rodrigues 301ad369c0 FEAT: networks get endpoint 2025-04-04 18:25:48 -03:00
Marcos Rodrigues Silva a2c1d79247 Merge pull request #276 from dadosfera/release/march
FIX: correct env
2025-04-02 17:06:25 -03:00
marcos-silva-rodrigues e5d9ac2fbf FEAT: network policy endpoint 2025-04-02 16:48:10 -03:00
Marcos Rodrigues Silva 67a15b36b3 Merge pull request #275 from dadosfera/release/march
FIX: correct env
2025-04-02 14:07:52 -03:00
marcos-silva-rodrigues f396cdb2d3 FIX: correct env 2025-04-02 14:01:54 -03:00
marcos-silva-rodrigues 9cc538cdcd FEAT: add host in metadata 2025-04-01 17:57:05 -03:00
Guilherme Maioli 01da6becff Merge pull request #274 from dadosfera/UPDATE/helmfile
UPDATE: return variable to stg.
2025-03-31 14:08:00 -03:00
Guilherme Maioli 003c9b176c UPDATE: return variable to stg. 2025-03-31 14:04:34 -03:00
Marcos Rodrigues Silva 0a5315fb95 Merge pull request #273 from dadosfera/feature/block_and_reset_users
FEAT: block, unblock and reset users
2025-03-28 17:00:49 -03:00
marcos-silva-rodrigues 5a4d578d13 FEAT: block, unblock and reset users 2025-03-28 12:30:55 -03:00
Marcos Rodrigues Silva 60214f0e57 Merge pull request #272 from dadosfera/feature/block_and_reset_users
Feature/block and reset users
2025-03-24 16:27:35 -03:00
marcos-silva-rodrigues 3a2ce72910 UPDATE: protospack 2025-03-24 14:46:24 -03:00
marcos-silva-rodrigues 074d3ce201 BUILD: remove unused packages 2025-03-21 18:09:09 -03:00
marcos-silva-rodrigues 05c80e2497 FEAT: reset, block and unblock all users 2025-03-21 17:04:58 -03:00
marcos-silva-rodrigues 8a363dd815 FIX: Merge main 2025-03-18 17:59:04 -03:00
Marcos Rodrigues Silva 1e5fe59d9c Merge pull request #271 from dadosfera/feature/new-mixpanel-properties
Add new mixpanel properties
2025-03-14 15:20:42 -03:00
Jonathan Witkosky 1a2b9617fe Add new mixpanel properties 2025-03-14 13:57:31 -03:00
Guilherme Maioli 1f92510a43 Merge pull request #270 from dadosfera/bugfixes/13-03
Bugfixes/13 03
2025-03-13 17:59:32 -03:00
marcos-silva-rodrigues f7fceb1ea2 Merge branch 'fix/white-label' into bugfixes/13-03 2025-03-13 17:41:12 -03:00
Rafael Moraes 2aec60a354 FEATURE: Reset and block users 2025-03-13 14:44:54 -03:00
Marcos Rodrigues Silva 9bc42a4af0 Merge pull request #269 from dadosfera/fix/private-route
FIX: cors list
2025-02-26 17:15:03 -03:00
marcos-silva-rodrigues 5c98cc1748 FIX: cors list 2025-02-26 17:13:57 -03:00
Marcos Rodrigues Silva 9ae14ab04a Merge pull request #268 from dadosfera/fix/private-route
FIX: correct ip
2025-02-26 16:00:20 -03:00
marcos-silva-rodrigues 5d014f4669 FIX: correct ip 2025-02-26 15:57:07 -03:00
Marcos Rodrigues Silva 5e8dea7255 Merge pull request #267 from dadosfera/fix/private-route
FIX: add ip range and domain list to route /open-data
2025-02-26 08:49:40 -03:00
marcos-silva-rodrigues 38fc9f521b FIX: add ip range and domain list to route /open-data 2025-02-25 17:10:39 -03:00
Marcos Rodrigues Silva 80d7ac6ff9 Merge pull request #264 from dadosfera/fix/white-label
Fix/white label
2025-02-24 09:35:37 -03:00
marcos-silva-rodrigues 9bacd7a0f5 UPDATE: merge with beta branch 2025-02-24 09:28:03 -03:00
Aldemir Humberto Soares Neto c89ac40039 Merge pull request #266 from dadosfera/fixing-build-ecr
FEAT: fixing package-lock
2025-02-22 07:42:46 -03:00
aldemirneto 6d6cf590ee FEAT: fixing package-lock 2025-02-22 07:39:41 -03:00
Aldemir Humberto Soares Neto f063a8f30e Merge pull request #265 from dadosfera/UNIVR-PII-FACILITIES
Univr pii facilities
2025-02-22 07:29:14 -03:00
aldemirneto 03536a2180 FEAT: ensuring compatibility with ^ in the version 2025-02-22 07:28:30 -03:00
aldemirneto 0efd5cc257 FEAT: Changing protospack version
PII enabling
2025-02-22 07:23:34 -03:00
marcos-silva-rodrigues 9439f13b7b UPdATE: new displayName prop 2025-02-21 17:31:49 -03:00
Rafael Santana d936806406 Merge pull request #262 from dadosfera/beta
[PRD] Enable Dataset Registration via API for External Sources
2025-02-20 14:03:56 -03:00
Rafael Santana 9d7ccd4201 Merge pull request #263 from dadosfera/feat/catalog-external-datasets-using-api-call
UPDATE: migrating maestro to backend nodepool
2025-02-20 13:47:44 -03:00
Rafael c8a4c1aae6 UPDATE: migrating maestro to backend nodepool 2025-02-20 13:46:30 -03:00
Rafael Santana 406db7ccff Merge pull request #261 from dadosfera/feat/catalog-external-datasets-using-api-call
UPDATE: improve logging for table metadata
2025-02-20 08:52:15 -03:00
Rafael f48a35c7ce UPDATE: improve logging for table metadata 2025-02-20 08:40:48 -03:00
Rafael Santana 8d25df74b7 Merge pull request #260 from dadosfera/feat/catalog-external-datasets-using-api-call
UPDATE: updating protospack for add table_schema for /catalog/registe…
2025-02-19 12:25:10 -03:00
Rafael 831a03402e UPDATE: updating protospack for add table_schema for /catalog/register-dataset rout 2025-02-19 12:19:59 -03:00
Marcos Rodrigues Silva a631338599 Merge pull request #259 from dadosfera/feat/update-customer-dash
UPDATE: customer dash
2025-02-17 19:02:52 -03:00
Marcos Rodrigues Silva d943721b4a Merge pull request #258 from dadosfera/feat/update-customer-dash
Feat/update customer dash
2025-02-17 18:47:11 -03:00
Rafael Santana 1bbf978cc4 Merge pull request #257 from dadosfera/feat/catalog-external-datasets-using-api-call
FEAT: Enable dataset registration via API for external sources
2025-02-14 16:57:58 -03:00
Rafael 856f913ce6 Merge branch 'feat/catalog-external-datasets-using-api-call' of github.com:dadosfera/maestro into feat/catalog-external-datasets-using-api-call 2025-02-14 15:07:13 -03:00
Rafael dc617b6925 REFACTOR: Improve logging with additional metadata
- Enhanced logs to include more detailed request body information.
- Standardized log calls to include extra metadata, such as service name, environment, customer name, user ID, HTTP method, and request path.
- Improved error logging to provide better debugging insights.

Example log format:
EXTRA[{"service":{"name":"maestro","environment":"stg"},"customer_name":"dadosfera","user_id":"047824d8-0021-709a-bf15-39b8dd069aaf","method":"POST","path":"/catalog/register-dataset"}]

Related tickets or issues: [TECN-9441]
2025-02-14 15:04:22 -03:00
Rafael Santana 3f80062672 Merge branch 'beta' into feat/catalog-external-datasets-using-api-call 2025-02-14 10:14:01 -03:00
Rafael 07eb423814 UPDATE: update version of protospack 2025-02-14 10:12:20 -03:00
Rafael 721232448e FEAT: Enable dataset registration via API for external sources
As part of our hybrid approach, we need to allow dataset cataloging from outside our cloud environment.
Instead of relying on our GRPC APIs, we leveraged an existing REST API to handle this process.

- Updated Maestro to call Nimbus directly for dataset registration.
- Modified protospack to generate the necessary TypeScript interfaces.
- Added the `/catalog/register-dataset` endpoint to the API.

This change enables seamless dataset registration from external sources while maintaining compatibility with our existing infrastructure.

Related tickets or issues: [TECN-9441]
2025-02-14 10:08:45 -03:00
Marcos Rodrigues Silva 0b0d261aae Merge pull request #256 from dadosfera/fix/white-label
UPDATE: remove user agent
2025-02-13 13:56:37 -03:00
marcos-silva-rodrigues 87a8d741d7 UPDATE: remove user agent 2025-02-13 13:55:37 -03:00
marcos-silva-rodrigues d03dd6129a UPDATE: customer dash 2025-02-13 12:20:04 -03:00
Marcos Rodrigues Silva 96c20995a3 Merge pull request #255 from dadosfera/beta
Beta
2025-02-03 18:22:05 -03:00
Marcos Rodrigues Silva 82470553db Merge pull request #254 from dadosfera/fix/white-label
FIX: remove support for svg
2025-02-03 12:29:19 -03:00
marcos-silva-rodrigues 2408505889 FIX: remove support for svg 2025-02-03 12:25:56 -03:00
Marcos Rodrigues Silva 7d85693a1f Merge pull request #253 from dadosfera/fix/white-label
Fix/white label
2025-02-03 09:53:38 -03:00
marcos-silva-rodrigues d86a011bfd Reapply "Merge pull request #250 from dadosfera/beta"
This reverts commit d3f7664116.
2025-02-01 16:49:18 -03:00
Marcos Rodrigues Silva 4d54ac744b Merge pull request #252 from dadosfera/feature/new-monitoring-dash
FEAT: update monitoring dash
2025-01-31 18:32:41 -03:00
marcos-silva-rodrigues 132614b551 FEAT: update monitoring dash 2025-01-31 18:24:08 -03:00
Rafael Santana 17c541548a Merge pull request #251 from dadosfera/rollback-main
Revert "Merge pull request #250 from dadosfera/beta"
2025-01-31 18:20:20 -03:00
Rafael d3f7664116 Revert "Merge pull request #250 from dadosfera/beta"
This reverts commit 2ea40004a6, reversing
changes made to 873f7ea314.
2025-01-31 18:13:33 -03:00
Marcos Rodrigues Silva 2ea40004a6 Merge pull request #250 from dadosfera/beta
Beta
2025-01-31 17:24:43 -03:00
Marcos Rodrigues Silva 82ec2d9493 Merge pull request #248 from dadosfera/feature/new-monitoring-dash
FEAT: change dashboard id
2025-01-31 14:40:02 -03:00
Marcos Rodrigues Silva 53bef174b3 Merge pull request #249 from dadosfera/feature/white-label-app
FIX: return null when not found theme
2025-01-31 13:40:07 -03:00
marcos-silva-rodrigues bcfef8359e FIX: return null when not found theme 2025-01-31 13:39:15 -03:00
Marcos Rodrigues Silva 5f18411aed Merge pull request #247 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-30 17:57:09 -03:00
marcos-silva-rodrigues 1102a6e8e5 FEAT: remove personal data in logger and only endpoint for wordpress 2025-01-30 17:06:09 -03:00
marcos-silva-rodrigues 867aec092b FEAT: add svg 2025-01-30 16:06:13 -03:00
marcos-silva-rodrigues ea9e727fcf FEAT: change dashboard id 2025-01-30 15:09:27 -03:00
Marcos Rodrigues Silva 099476f618 Merge pull request #246 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-27 15:07:22 -03:00
marcos-silva-rodrigues db78494b1a FIX: test log headers 2025-01-27 14:39:12 -03:00
marcos-silva-rodrigues e7ad6e5a7b CHORE: update env 2025-01-27 14:12:38 -03:00
Marcos Rodrigues Silva 3d7d051df2 Merge pull request #245 from dadosfera/feature/white-label-app
chore: update stg values
2025-01-27 11:50:43 -03:00
marcos-silva-rodrigues 8e827dc934 chore: update stg values 2025-01-27 11:47:23 -03:00
Marcos Rodrigues Silva 12b764710a Merge pull request #244 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-27 09:49:29 -03:00
marcos-silva-rodrigues b5c95874bb chore: updated package 2025-01-27 09:14:42 -03:00
marcos-silva-rodrigues b896b1a9b8 REFACTOR: rename props 2025-01-26 16:44:31 -03:00
marcos-silva-rodrigues cf99acc682 FEAT: send images in stream 2025-01-26 16:37:37 -03:00
Marcos Rodrigues Silva 7fb20964dc Merge pull request #243 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-23 08:39:26 -03:00
Rafael Santana 8751117946 Merge pull request #242 from dadosfera/decrease-replica-count-maestro
UPDATE: add toleration for spot instances
2025-01-22 11:24:35 -03:00
marcos-silva-rodrigues 8e13541b24 FEAT: update protospack 2025-01-21 17:28:13 -03:00
marcos-silva-rodrigues 4f6a9d4b66 FEAT: new property in customer obj 2025-01-21 17:00:34 -03:00
Rafael Santana 56411496c2 Merge pull request #241 from dadosfera/decrease-replica-count-maestro
Decrease replica count maestro
2025-01-20 18:50:14 -03:00
Rafael Santana 1626d85d31 Merge pull request #240 from dadosfera/decrease-replica-count-maestro
UPDATE: decrease the number of replicas for stg environment
2025-01-20 18:41:54 -03:00
48 changed files with 5312 additions and 1747 deletions
+24 -1
View File
@@ -4,9 +4,19 @@ FROM base_image AS build_base
WORKDIR /app WORKDIR /app
RUN apk update RUN apk update
# needed packages to build dependencies from source # needed packages to build dependencies from source
RUN apk add --no-cache aws-cli RUN apk add --no-cache \
aws-cli \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont
COPY package*.json ./ COPY package*.json ./
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# run aws cli without mounting secret, because CI already has AWS credentials # run aws cli without mounting secret, because CI already has AWS credentials
FROM build_base AS ci_image FROM build_base AS ci_image
@@ -40,4 +50,17 @@ WORKDIR /app
COPY --from=prod_build /app/dist ./dist COPY --from=prod_build /app/dist ./dist
COPY --from=prod_build /app/node_modules ./node_modules COPY --from=prod_build /app/node_modules ./node_modules
COPY --from=prod_build /app/package*.json ./ COPY --from=prod_build /app/package*.json ./
RUN apk update
# needed packages to build dependencies from source
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENTRYPOINT npm run start:prod ENTRYPOINT npm run start:prod
+455 -5
View File
@@ -64,6 +64,14 @@
], ],
"type": "string" "type": "string"
} }
},
{
"name": "origin",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
} }
], ],
"requestBody": { "requestBody": {
@@ -249,6 +257,14 @@
], ],
"type": "string" "type": "string"
} }
},
{
"name": "origin",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
} }
], ],
"responses": { "responses": {
@@ -542,6 +558,123 @@
] ]
} }
}, },
"/auth/users/block": {
"post": {
"operationId": "AuthController_blockUsers",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Auth"
],
"security": [
{
"access-token": []
}
]
}
},
"/auth/users/unblock": {
"post": {
"operationId": "AuthController_unblockUsers",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Auth"
],
"security": [
{
"access-token": []
}
]
}
},
"/auth/users/reset": {
"post": {
"operationId": "AuthController_resetUsers",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Auth"
],
"security": [
{
"access-token": []
}
]
}
},
"/connections": { "/connections": {
"post": { "post": {
"operationId": "ConnectionController_createConnection", "operationId": "ConnectionController_createConnection",
@@ -4787,6 +4920,87 @@
] ]
} }
}, },
"/catalog/register-dataset": {
"post": {
"operationId": "CatalogController_registerDatasetWithMetadataRequest",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
}
],
"responses": {
"201": {
"description": ""
}
},
"tags": [
"Catalog"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/catalog/pii-reporter": {
"get": {
"operationId": "CatalogController_getPiiReporter",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
},
{
"name": "type",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Catalog"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/terms-of-use/token": { "/terms-of-use/token": {
"get": { "get": {
"operationId": "TermsOfUseController_getToken", "operationId": "TermsOfUseController_getToken",
@@ -5297,6 +5511,57 @@
] ]
} }
}, },
"/customers/{id}/mfa": {
"post": {
"operationId": "CustomersController_enableMfaEnforce",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EnforceMfa"
}
}
}
},
"responses": {
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/customers/{id}/links": { "/customers/{id}/links": {
"get": { "get": {
"operationId": "CustomersController_getCustomerLinks", "operationId": "CustomersController_getCustomerLinks",
@@ -5431,6 +5696,56 @@
] ]
} }
}, },
"/customers/logs-dashboard": {
"get": {
"operationId": "CustomersController_getCustomerMixPanelLogsDashboard",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/customers/access-dashboard": {
"get": {
"operationId": "CustomersController_getAccessDashboard",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/open-data/sharing-ocean-data": { "/open-data/sharing-ocean-data": {
"post": { "post": {
"operationId": "OpenDataController_createUser", "operationId": "OpenDataController_createUser",
@@ -5454,6 +5769,14 @@
"schema": { "schema": {
"type": "string" "type": "string"
} }
},
{
"name": "origin",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
} }
], ],
"responses": { "responses": {
@@ -5555,6 +5878,96 @@
"Health" "Health"
] ]
} }
},
"/network-policy": {
"get": {
"operationId": "NetworkPolicyController_getNetworks",
"parameters": [],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NetworkPoliciesDTO"
}
}
}
}
},
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
},
"post": {
"operationId": "NetworkPolicyController_applyNetworkPolicies",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NetworkPoliciesDTO"
}
}
}
},
"responses": {
"200": {
"description": ""
},
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
},
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
},
"delete": {
"operationId": "NetworkPolicyController_removeNetworkPolicies",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NetworkPoliciesDTO"
}
}
}
},
"responses": {
"200": {
"description": ""
}
},
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
} }
}, },
"info": { "info": {
@@ -5608,6 +6021,9 @@
"name": { "name": {
"type": "string" "type": "string"
}, },
"displayName": {
"type": "string"
},
"tier": { "tier": {
"type": "string" "type": "string"
}, },
@@ -5619,15 +6035,24 @@
"items": { "items": {
"type": "string" "type": "string"
} }
},
"themeEnabled": {
"type": "boolean"
},
"enforceMfa": {
"type": "boolean"
} }
}, },
"required": [ "required": [
"modules", "modules",
"id", "id",
"name", "name",
"displayName",
"tier", "tier",
"scheduleLimit", "scheduleLimit",
"links" "links",
"themeEnabled",
"enforceMfa"
] ]
}, },
"AuthUser": { "AuthUser": {
@@ -8296,6 +8721,17 @@
"tags" "tags"
] ]
}, },
"EnforceMfa": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
},
"required": [
"enabled"
]
},
"CustomerLink": { "CustomerLink": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -8374,18 +8810,18 @@
"textColor": { "textColor": {
"type": "string" "type": "string"
}, },
"logoWhite": { "logo": {
"type": "string" "type": "string"
}, },
"logoBlack": { "logoLogin": {
"type": "string" "type": "string"
} }
}, },
"required": [ "required": [
"backgroundColor", "backgroundColor",
"textColor", "textColor",
"logoWhite", "logo",
"logoBlack" "logoLogin"
] ]
}, },
"CustomerThemeResponse": { "CustomerThemeResponse": {
@@ -8398,6 +8834,20 @@
"required": [ "required": [
"theme" "theme"
] ]
},
"NetworkPoliciesDTO": {
"type": "object",
"properties": {
"policies": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"policies"
]
} }
} }
} }
+4 -2
View File
@@ -15,6 +15,8 @@ charts:
- name: maestro.tr_factory_url - name: maestro.tr_factory_url
value: in-factory.dadosfera.ai value: in-factory.dadosfera.ai
- name: maestro.open_customer_id - name: maestro.open_customer_id
value: f239718a-a271-4ef9-ae7e-02a2f0f3aa6e value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id - name: maestro.open_group_id
value: 401573bb-334f-44b2-b30e-88d4cea31ae9 value: c0afdcce-c5be-40d0-9d1d-2d271121f14a
- name: replicaCount
value: 2
+1 -1
View File
@@ -11,7 +11,7 @@ charts:
- name: maestro.pi_factory_url - name: maestro.pi_factory_url
value: pi-factory.dadosfera.ai value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url - name: maestro.in_factory_url
value: in-factory.dadosfera.ai value: in-factory.stg.dadosfera.ai
- name: maestro.tr_factory_url - name: maestro.tr_factory_url
value: in-factory.dadosfera.ai value: in-factory.dadosfera.ai
- name: maestro.open_customer_id - name: maestro.open_customer_id
+6 -1
View File
@@ -35,7 +35,8 @@ spec:
- key: application - key: application
operator: In operator: In
values: values:
- backend - general
tolerations: tolerations:
- key: "kubernetes.azure.com/scalesetpriority" - key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal" operator: "Equal"
@@ -95,6 +96,10 @@ spec:
value: {{ .Values.maestro.tr_factory_url }} value: {{ .Values.maestro.tr_factory_url }}
- name: UPLOAD_FILE_AGENT_CONNECTION - name: UPLOAD_FILE_AGENT_CONNECTION
value: {{ .Values.maestro.upload_file_agent_connection }} value: {{ .Values.maestro.upload_file_agent_connection }}
- name: OPEN_CUSTOMER_ID
value: {{ .Values.maestro.open_customer_id }}
- name: OPEN_GROUP_ID
value: {{ .Values.maestro.open_group_id }}
- name: JWT_PRIVATE_KEY - name: JWT_PRIVATE_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
+28
View File
@@ -0,0 +1,28 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "69.49.241.121/32" # hostgator ip
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/server-snippet: |
underscores_in_headers on;
ignore_invalid_headers on;
generation: 1
labels:
app: maestro
name: open-data
namespace: applications
spec:
ingressClassName: nginx
rules:
- host: {{ .Values.hostname }}
http:
paths:
- backend:
service:
name: maestro
port:
number: {{ .Values.ingress.port }}
path: /open-data/sharing-ocean-data
pathType: Prefix
+1 -1
View File
@@ -3,7 +3,7 @@
# Declare variables to be passed into your templates. # Declare variables to be passed into your templates.
replicaCount: 3 replicaCount: 3
hostname: maestro-temp.dadosfera.ai hostname: maestro.dadosfera.ai
image: image:
repository: 611330257153.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd repository: 611330257153.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
+1
View File
@@ -4,6 +4,7 @@
"compilerOptions": { "compilerOptions": {
"assets": [ "assets": [
"**/*.proto", "**/*.proto",
"assets/**/*",
{ {
"include": "i18n/**/*", "include": "i18n/**/*",
"watchAssets": true "watchAssets": true
+3637 -1617
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -30,7 +30,7 @@
"@aws-sdk/client-secrets-manager": "^3.414.0", "@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3", "@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.37.0-beta.3", "@dadosfera/protospack-v2": "3.37.0-beta.23",
"@grpc/grpc-js": "^1.9.3", "@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9", "@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0", "@nestjs/cli": "^9.5.0",
@@ -48,8 +48,10 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
"cron-parser": "^4.9.0", "cron-parser": "^4.9.0",
"csv": "^6.3.11",
"dotenv": "^14.3.2", "dotenv": "^14.3.2",
"elastic-apm-node": "^3.50.0", "elastic-apm-node": "^3.50.0",
"handlebars": "^4.7.8",
"helmet": "^5.1.1", "helmet": "^5.1.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"jwk-to-pem": "^2.0.5", "jwk-to-pem": "^2.0.5",
@@ -61,6 +63,7 @@
"passport-google-oauth20": "^2.0.0", "passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3", "passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0", "passport-mailchimp": "^1.1.0",
"puppeteer": "^24.7.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.5.5", "rxjs": "^7.5.5",
+3 -1
View File
@@ -28,6 +28,7 @@ import { MixpanelModule } from './modules/mixpanel/mixpanel.module';
import { CustomersModule } from './modules/customers/customers.module'; import { CustomersModule } from './modules/customers/customers.module';
import { OpenDataModule } from './modules/open-data/open-data.module'; import { OpenDataModule } from './modules/open-data/open-data.module';
import { ThemeModule } from './modules/theme/theme.module'; import { ThemeModule } from './modules/theme/theme.module';
import { NetworkPolicyModule } from './modules/network-policy/network-policy.module';
@Module({ @Module({
providers: [ providers: [
@@ -62,7 +63,8 @@ import { ThemeModule } from './modules/theme/theme.module';
OpenDataModule, OpenDataModule,
ThemeModule, ThemeModule,
//Always leave HealthModule last, so it is on the bottom of swagger //Always leave HealthModule last, so it is on the bottom of swagger
HealthModule HealthModule,
NetworkPolicyModule
], ],
}) })
export class AppModule {} export class AppModule {}
+130
View File
@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dadosfera Relatório de PII</title>
<link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;700&display=swap" rel="stylesheet">
<style>
@page {
size: A4 landscape; /* Alterado para paisagem (landscape) */
margin: 15mm 10mm; /* Reduzido para proporcionar mais espaço */
}
body {
font-family: 'Quicksand', sans-serif;
color: #5c5c5c;
margin: 0;
padding: 10px;
font-size: 12px; /* Reduzindo o tamanho da fonte */
}
.container {
margin: 0;
width: 100%;
}
.header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.logo {
max-width: 150px; /* Reduzida para economizar espaço */
height: auto;
}
h1 {
color: #0d003b;
font-weight: 700;
margin-left: 20px;
font-size: 24px; /* Tamanho ajustado */
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
table-layout: fixed; /* Importante: define larguras fixas */
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border: 1px solid #d0d0d0;
}
th {
background-color: #1700a2;
color: white;
font-weight: bold;
text-align: left;
padding: 8px 10px;
border: 1px solid #3a26b8;
font-size: 11px; /* Tamanho ajustado */
word-wrap: break-word; /* Permite quebra de palavras */
overflow-wrap: break-word;
}
td {
padding: 6px 10px;
border: 1px solid #d0d0d0;
font-size: 11px; /* Tamanho ajustado */
word-wrap: break-word; /* Permite quebra de palavras */
overflow-wrap: break-word;
}
/* Definindo larguras específicas para cada coluna */
th:nth-child(1), td:nth-child(1) { width: 14%; } /* Database */
th:nth-child(2), td:nth-child(2) { width: 14%; } /* Schema */
th:nth-child(3), td:nth-child(3) { width: 17%; } /* Tabela */
th:nth-child(4), td:nth-child(4) { width: 17%; } /* Coluna */
th:nth-child(5), td:nth-child(5) { width: 13%; } /* Tipo de Dado */
th:nth-child(6), td:nth-child(6) { width: 25%; } /* Regras PII */
tr:nth-child(even) {
background-color: #f9f9f9;
}
tr:nth-child(odd) {
background-color: white;
}
.info-section {
margin-top: 20px;
color: #5c5c5c;
}
.timestamp {
font-style: italic;
text-align: right;
margin-top: 15px;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="https://dadosfera.ai/wp-content/webp-express/webp-images/uploads/2022/06/Logo-Dadosfera1-1.png.webp" alt="Logo Dadosfera" class="logo">
<h1>Relatório de PII</h1>
</div>
<div class="info-section">
<p>Este relatório apresenta a estrutura de tabelas e suas as seguintes características de PII identificadas.</p>
</div>
<table>
<thead>
<tr>
<th>Database</th>
<th>Schema</th>
<th>Tabela</th>
<th>Coluna</th>
<th>Tipo de Dado</th>
<th>Regras PII</th>
</tr>
</thead>
<tbody>
{{#each dados}}
<tr>
<td>{{database_name}}</td>
<td>{{table_schema}}</td>
<td>{{table_name}}</td>
<td>{{column_name}}</td>
<td>{{data_type}}</td>
<td>{{pii_rules}}</td>
</tr>
{{/each}}
</tbody>
</table>
<p class="timestamp">Gerado em: {{dataGeracao}}</p>
</div>
</body>
</html>
@@ -121,6 +121,7 @@ export class AuthenticationGuard
customer_id: accessTokenPayload.customer_id, customer_id: accessTokenPayload.customer_id,
customer_name: accessTokenPayload.customer_name, customer_name: accessTokenPayload.customer_name,
customer_tier: accessTokenPayload.customer_tier, customer_tier: accessTokenPayload.customer_tier,
customer_modules: accessTokenPayload.customer_modules,
access_token: accessToken, access_token: accessToken,
}; };
// TODO: for backwards compatibility. remove in the future // TODO: for backwards compatibility. remove in the future
+8
View File
@@ -609,6 +609,14 @@ export interface DadosferaModule {
key: string; key: string;
permissionSeqId: number; permissionSeqId: number;
} }
export const DADOSFERA_MODULES_KEYS = {
LOG_DASHBOARD: 'logs-dashboard',
ACCESS_DASHBOARD: 'access-dashboard',
DANGER_ZONE: 'danger-zone',
PII: 'pii'
}
export const DADOSFERA_MODULES: Array<DadosferaModule> = [ export const DADOSFERA_MODULES: Array<DadosferaModule> = [
{ {
name: 'Intelligence Module', name: 'Intelligence Module',
@@ -25,6 +25,14 @@ export function RequireSomePermission(
); );
} }
export function RequireModule(
key: string
) {
return createAuthenticatedDecorator((_, user: RequestUser) =>
user.customer_modules.some(module => module === key),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) { export function AuthenticateCondition(func: AuthenticationFunction) {
return createAuthenticatedDecorator(func); return createAuthenticatedDecorator(func);
} }
-32
View File
@@ -1,32 +0,0 @@
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { SetMetadata } from '@nestjs/common';
export const SetOrigin = (origin: string) => SetMetadata('allowedOrigin', origin);
@Injectable()
export class CORSGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const response = context.switchToHttp().getResponse();
const request = context.switchToHttp().getRequest();
const origin = request.headers.origin;
// Obter a origem permitida através do decorador
const allowedOrigin = this.reflector.get<string>('allowedOrigin', context.getHandler());
if (process.env.ENV === "local") {
return true;
}
// Verifica se a origem da requisição é permitida
if (allowedOrigin && origin !== allowedOrigin) {
throw new ForbiddenException('Acesso não permitido pela política CORS');
}
response.setHeader('Access-Control-Allow-Origin', allowedOrigin);
return true;
}
}
+1
View File
@@ -11,6 +11,7 @@ export interface RequestUser {
customer_name: string; customer_name: string;
customer_tier: string; customer_tier: string;
access_token: string; access_token: string;
customer_modules: string[];
} }
export const User: (options?: { required?: boolean }) => ParameterDecorator = export const User: (options?: { required?: boolean }) => ParameterDecorator =
+3
View File
@@ -3,6 +3,7 @@ import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import helmet from 'helmet'; import helmet from 'helmet';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { json, urlencoded } from 'express';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
@@ -25,6 +26,8 @@ async function bootstrap() {
}, },
}); });
app.use(helmet()); app.use(helmet());
app.use('/catalog/register-dataset', json({ limit: '10mb' }));
app.use('/catalog/register-dataset', urlencoded({ extended: true, limit: '10mb' }));
configureSwagger(app); configureSwagger(app);
await app.listen(3333); await app.listen(3333);
if (process.env.KILL_AFTER_START) await app.close(); if (process.env.KILL_AFTER_START) await app.close();
+83 -7
View File
@@ -43,6 +43,7 @@ import {
AuthRefreshAccessTokenRes, AuthRefreshAccessTokenRes,
AuthSignInReq, AuthSignInReq,
AuthSignInRes, AuthSignInRes,
BulkEditRequest,
} from './dtos/login'; } from './dtos/login';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
@@ -85,9 +86,12 @@ export class AuthController {
async signIn( async signIn(
@Body() { username, password, totp }: AuthSignInReq, @Body() { username, password, totp }: AuthSignInReq,
@Language() language: LanguageEnum, @Language() language: LanguageEnum,
@Headers('origin') origin = '',
): Promise<AuthSignInRes> { ): Promise<AuthSignInRes> {
this.logger.info('/auth - SignIn'); this.logger.info('/auth - SignIn');
const metadata = PackTheMetadata({ language }); const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({ language, custom_host: frontHost });
this.logger.info('metadata: ' + JSON.stringify(metadata.toJSON()));
return this.authClient.signIn({ username, password, totp }, metadata); return this.authClient.signIn({ username, password, totp }, metadata);
} }
@@ -97,13 +101,15 @@ export class AuthController {
async refreshAccessToken( async refreshAccessToken(
@Body() body: AuthRefreshAccessTokenReq, @Body() body: AuthRefreshAccessTokenReq,
@Language() language: LanguageEnum, @Language() language: LanguageEnum,
@Headers('origin') origin: string,
) { ) {
this.logger.info('/auth - RefreshAccessToken'); this.logger.info('/auth - RefreshAccessToken');
const { refreshToken, customerName: customer_name } = body; const { refreshToken, customerName: customer_name } = body;
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({ const metadata = PackTheMetadata({
customer_name,
language, language,
customer_name,
custom_host: frontHost,
}); });
return this.authClient.refreshAccessToken({ refreshToken }, metadata); return this.authClient.refreshAccessToken({ refreshToken }, metadata);
@@ -177,16 +183,23 @@ export class AuthController {
@ApiInternalOnlyEndpoint() @ApiInternalOnlyEndpoint()
@Post('confirm-reset-password') @Post('confirm-reset-password')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) { async confirmResetPassword(
@Body() body: AuthConfirmResetPasswordRequest,
@Headers('origin') origin: string,
) {
this.logger.info('/auth - confirm-reset-password'); this.logger.info('/auth - confirm-reset-password');
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({ custom_host: frontHost });
const { username, code, newPassword } = body; const { username, code, newPassword } = body;
return this.authClient.confirmResetPassword({ return this.authClient.confirmResetPassword(
{
username, username,
code, code,
newPassword, newPassword,
}); },
metadata,
);
} }
@ApiInternalOnlyEndpoint() @ApiInternalOnlyEndpoint()
@@ -344,4 +357,67 @@ export class AuthController {
return { token, email, url, language }; return { token, email, url, language };
} }
@ApiInternalOnlyEndpoint()
@Post('users/block')
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
async blockUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest,
) {
this.logger.info('blockUsers - Starting request');
try {
const metadata = PackTheMetadata(user);
this.logger.debug('Calling blockUsers service', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language'),
},
});
const result = await this.authClient.blockUsers(body.users, metadata);
this.logger.info('blockUsers - Success', { result });
return result;
} catch (error) {
this.logger.error('blockUsers - Error', {
error: error.message,
stack: error.stack,
});
throw error;
}
}
@ApiInternalOnlyEndpoint()
@Post('users/unblock')
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
async unblockUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest,
) {
this.logger.info('unblockUsers');
const metadata = PackTheMetadata(user);
return this.authClient.unblockUsers(body.users, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('users/reset')
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
async resetUsers(
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Body() body: BulkEditRequest,
) {
this.logger.info('resetUsers');
const metadata = PackTheMetadata(user);
return this.authClient.resetUsers(body.users, metadata);
}
} }
+78 -7
View File
@@ -20,6 +20,7 @@ import {
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages'; } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DucClient } from '../duc/client.config'; import { DucClient } from '../duc/client.config';
import { Metadata } from '@grpc/grpc-js'; import { Metadata } from '@grpc/grpc-js';
import { BulkEditResponse } from './dtos/login';
@Injectable() @Injectable()
export class AuthClientService implements OnModuleInit { export class AuthClientService implements OnModuleInit {
@@ -112,19 +113,21 @@ export class AuthClientService implements OnModuleInit {
); );
} }
async confirmResetPassword({ async confirmResetPassword(
username, { username, code, newPassword }: AuthConfirmResetPasswordRequest,
code, metadata: Metadata,
newPassword, ) {
}: AuthConfirmResetPasswordRequest) {
this.logger.info('confirmResetPassword'); this.logger.info('confirmResetPassword');
return lastValueFrom( return lastValueFrom(
this.authService.AuthConfirmResetPassword({ this.authService.AuthConfirmResetPassword(
{
username, username,
code, code,
newPassword, newPassword,
}), },
metadata,
),
); );
} }
@@ -168,4 +171,72 @@ export class AuthClientService implements OnModuleInit {
this.authService.AuthOauthSignIn({ token, username, refreshToken: '' }), this.authService.AuthOauthSignIn({ token, username, refreshToken: '' }),
); );
} }
async blockUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('blockUsers - Service starting');
try {
this.logger.debug('Calling BlockUser gRPC method', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language'),
},
});
const response = await lastValueFrom<BulkEditResponse>(
this.authService.BlockUser({ users }, metadata),
);
this.logger.info('blockUsers - Service success', { response });
return response;
} catch (error) {
this.logger.error('blockUsers - Service error', {
error: error.message,
stack: error.stack,
});
throw error;
}
}
async unblockUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('unblockUsers');
return await lastValueFrom(
this.authService.UnblockUser({ users }, metadata),
);
}
async resetUsers(
users: string[],
metadata: Metadata,
): Promise<BulkEditResponse> {
this.logger.info('resetUsers');
try {
this.logger.debug('Calling ResetUser gRPC method', {
metadata: {
access_token: metadata.get('access_token'),
language: metadata.get('language'),
},
});
const response = await lastValueFrom<BulkEditResponse>(
this.authService.ResetUser({ users }, metadata),
);
this.logger.info('resetUsers - Success', { response });
return response;
} catch (error) {
this.logger.error('resetUsers - Error', {
error: error.message,
stack: error.stack,
});
throw error;
}
}
} }
+22
View File
@@ -67,16 +67,28 @@ export class AuthUser {
export class AuthCustomer { export class AuthCustomer {
@ApiProperty() @ApiProperty()
modules: string[]; modules: string[];
@ApiProperty() @ApiProperty()
id: string; id: string;
@ApiProperty() @ApiProperty()
name: string; name: string;
@ApiProperty()
displayName: string;
@ApiProperty() @ApiProperty()
tier: string; tier: string;
@ApiProperty() @ApiProperty()
scheduleLimit: string; scheduleLimit: string;
@ApiProperty() @ApiProperty()
links: Link[]; links: Link[];
@ApiProperty()
themeEnabled: boolean;
@ApiProperty()
enforceMfa: boolean;
} }
export class AuthSignInReq implements AuthSignInRequest { export class AuthSignInReq implements AuthSignInRequest {
@@ -118,3 +130,13 @@ export class AuthRefreshAccessTokenRes {
@ApiProperty() @ApiProperty()
accessToken: string; accessToken: string;
} }
export interface BulkEditRequest {
users: string[];
}
export interface BulkEditResponse {
message: string;
successfulUsers: string[];
failedUsers: string[];
}
+3
View File
@@ -5,6 +5,7 @@ import {
} from '@nestjs/microservices'; } from '@nestjs/microservices';
import { credentials } from '@grpc/grpc-js'; import { credentials } from '@grpc/grpc-js';
import { Catalog } from '@dadosfera/protospack-v2'; import { Catalog } from '@dadosfera/protospack-v2';
import { PlatformInterfaces } from '@dadosfera/protospack-v2';
const isLocalConnection = const isLocalConnection =
process.env.PIFACTORY_URL.startsWith('pi-factory:') || process.env.PIFACTORY_URL.startsWith('pi-factory:') ||
@@ -19,11 +20,13 @@ export class CatalogClientConfiguration {
package: [ package: [
Catalog.ProtoPackages.ReadPackage, Catalog.ProtoPackages.ReadPackage,
Catalog.ProtoPackages.WritePackage, Catalog.ProtoPackages.WritePackage,
PlatformInterfaces.ProtoPackages.WritePackage
], ],
credentials: isLocalConnection ? undefined : credentials.createSsl(), credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ protoPath: [
Catalog.ProtoPaths.ReadFilePath, Catalog.ProtoPaths.ReadFilePath,
Catalog.ProtoPaths.WriteFilePath, Catalog.ProtoPaths.WriteFilePath,
PlatformInterfaces.ProtoPaths.WriteFilePath
], ],
loader: { loader: {
keepCase: true, keepCase: true,
+144 -2
View File
@@ -13,6 +13,9 @@ import {
Put, Put,
Query, Query,
UseFilters, UseFilters,
HttpException,
HttpStatus,
Res,
} from '@nestjs/common'; } from '@nestjs/common';
import { import {
ApiCreatedResponse, ApiCreatedResponse,
@@ -23,9 +26,10 @@ import {
import { import {
Authenticated, Authenticated,
RequireAllPermissions, RequireAllPermissions,
RequireModule,
RequireSomePermission, RequireSomePermission,
} from '../../decorators/authentication.decorator'; } from '../../decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum'; import { DADOSFERA_MODULES_KEYS, PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
import { CatalogService } from './catalog.service'; import { CatalogService } from './catalog.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
@@ -54,7 +58,10 @@ import {
AddRlsRuleRequest, AddRlsRuleRequest,
GetNimbusDashboardsRequest, GetNimbusDashboardsRequest,
GetRlsRulesRequest, GetRlsRulesRequest,
RegisterDatasetWithMetatadaRequest,
} from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages'; } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
import { Response } from 'express';
import { TypeParser } from 'src/utils/FileParser/parser-types';
@ApiTags('Catalog') @ApiTags('Catalog')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }]) @ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@@ -340,7 +347,7 @@ export class CatalogController {
@Language() language: LanguageEnum, @Language() language: LanguageEnum,
@Param('id') id: string, @Param('id') id: string,
): Promise<IPreviewResponse> { ): Promise<IPreviewResponse> {
const { customer_name, customer_id, user_id, username } = user; const { customer_name, customer_id, user_id, username, customer_modules } = user;
this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, { this.logger.info(`/catalog - ON GET DATA DOCS ROUTE`, {
user_id, user_id,
@@ -353,6 +360,7 @@ export class CatalogController {
user_id, user_id,
username, username,
language, language,
is_mask: customer_modules.some(mod => mod === 'pii')
}); });
const preview = await this.catalogService.getDatasetPreview(id, metadata); const preview = await this.catalogService.getDatasetPreview(id, metadata);
@@ -743,4 +751,138 @@ export class CatalogController {
return JSON.parse(dashboards); return JSON.parse(dashboards);
} }
@Post('register-dataset')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.CREATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async registerDatasetWithMetadataRequest(
@Body() body: RegisterDatasetWithMetatadaRequest,
@User() user: RequestUser,
) {
const { customer_id, customer_name, user_id, username } = user;
const logMetadata = {
customer_name: customer_name,
user_id: user_id,
method: 'POST',
path: '/catalog/register-dataset',
};
try {
const metadata = PackTheMetadata({
customer_id,
customer_name,
user_id,
username,
});
this.logger.log(
`Request from user ${user_id} for customer ${customer_name}`,
logMetadata,
);
// Create table metadata
const tableMetadataBody = {
table_metadata: body.table_metadata,
info: {
customer: customer_name,
},
logMetadata: logMetadata,
};
const table_metadata_id = await this.catalogService.createTableMetadata(
tableMetadataBody,
);
this.logger.info(`table_metadata_id: ${table_metadata_id}`, logMetadata);
// Create column metadata
const columnMetadataBody = {
column_metadata: body.column_metadata,
info: {
customer: customer_name,
},
};
this.logger.info(
`Creating column metadata for table ${table_metadata_id}`,
logMetadata,
);
const column_metadata_ids =
await this.catalogService.createColumnMetadata(columnMetadataBody);
// Create data preview
const dataPreviewBody = {
data_preview: body.data_preview,
info: {
customer: customer_name,
},
};
this.logger.debug(
`Creating data preview for table ${table_metadata_id}`,
logMetadata,
);
const data_preview_id = await this.catalogService.createDataPreview(
dataPreviewBody,
);
// Catalog dataset item
this.logger.info(
`Cataloging dataset item for table ${table_metadata_id}`,
logMetadata,
);
await this.catalogService.catalogDatasetItem(table_metadata_id, metadata);
this.logger.info(
`Dataset registration completed successfully for table ${table_metadata_id}`,
logMetadata,
);
return {
message: 'Dataset registered successfully',
table_metadata_id: table_metadata_id,
column_metadata_ids: column_metadata_ids,
data_preview_id: data_preview_id,
};
} catch (error) {
this.logger.error(
`Failed to register dataset. The following error occurred: ${error.response.data}`,
logMetadata,
);
throw new HttpException(
{
message: 'Ocorreu um erro ao registrar o dataset',
error: error.message,
code: 'REGISTRATION_FAILED',
details: error.message,
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@Get('pii-reporter')
@RequireSomePermission(
PERMISSIONS_GROUPS.USERS.permissions.ADMIN
)
@RequireModule(
DADOSFERA_MODULES_KEYS.PII
)
async getPiiReporter(@User() user: RequestUser, @Res() res: Response, @Query('type') contentType: TypeParser = "pdf") {
this.logger.info('GET pii-reporter');
const metadata = PackTheMetadata(user);
try {
const {
file,
filename,
type
} = await this.catalogService.getPiiReporter(metadata, contentType);
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.setHeader('Content-Type', type);
// use res.end to send buffer
return res.end(file);
} catch (error) {
console.error(error)
this.logger.error(error.message);
}
}
} }
+162
View File
@@ -5,6 +5,11 @@ import {
ProtoServices, ProtoServices,
Messages, Messages,
} from '@dadosfera/protospack-v2/dist/lib/Catalog'; } from '@dadosfera/protospack-v2/dist/lib/Catalog';
import {
Messages as PlatformInterfaceMessages,
WriteService as PlatformInterfaceWriteService,
ProtoServices as PlatformInterfacesProtoServices,
} from '@dadosfera/protospack-v2/dist/lib/PlatformInterfaces';
import { import {
BadRequestException, BadRequestException,
HttpException, HttpException,
@@ -28,11 +33,21 @@ import {
AddRlsRuleRequest, AddRlsRuleRequest,
GetNimbusDashboardsRequest, GetNimbusDashboardsRequest,
GetRlsRulesRequest, GetRlsRulesRequest,
PiiMetadata,
} from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages'; } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
import { writeFileSync } from 'fs';
import path from 'path';
import { HtmlParser } from 'src/utils/FileParser/html-parser';
import { PiiDto } from './dtos/pii.dto';
import { CsvParser } from 'src/utils/FileParser/csv-parser';
import { PDFParser } from 'src/utils/FileParser/pdf-parser';
import { TypeParser } from 'src/utils/FileParser/parser-types';
import { ParserBuilder } from 'src/utils/FileParser/parser.builder';
class CatalogService implements OnModuleInit { class CatalogService implements OnModuleInit {
catalogReadService: ReadService.CatalogReadServices; catalogReadService: ReadService.CatalogReadServices;
catalogWriteService: WriteService.CatalogWriteServices; catalogWriteService: WriteService.CatalogWriteServices;
platformWriteService: PlatformInterfaceWriteService.PlatformInterfacesWriteServices;
logger: any; logger: any;
constructor( constructor(
@Inject(DadosferaLogger) @Inject(DadosferaLogger)
@@ -54,9 +69,14 @@ class CatalogService implements OnModuleInit {
this.grpcClient.getService<WriteService.CatalogWriteServices>( this.grpcClient.getService<WriteService.CatalogWriteServices>(
ProtoServices.CatalogWriteServices, ProtoServices.CatalogWriteServices,
); );
this.platformWriteService =
this.grpcClient.getService<PlatformInterfaceWriteService.PlatformInterfacesWriteServices>(
PlatformInterfacesProtoServices.PlatformInterfacesWriteServices,
);
} }
_getNimbusUrl(body) { _getNimbusUrl(body) {
this.logger.debug(`Body: ${JSON.stringify(body)}`);
const customer = body.info.customer.toLowerCase(); const customer = body.info.customer.toLowerCase();
if (process.env.ENV === 'prd') { if (process.env.ENV === 'prd') {
@@ -69,6 +89,42 @@ class CatalogService implements OnModuleInit {
)}.dadosfera.ai`; )}.dadosfera.ai`;
} }
async getPiiReporter(metadata: Metadata, type: TypeParser) {
this.logger.info('getPiiReporter: ' + type)
try {
const {
data
} = await lastValueFrom(
this.catalogWriteService.GetPiiReporter({}, metadata)
)
this.logger.info("Finish grpc call")
const parser = ParserBuilder.build<PiiMetadata>(type);
this.logger.info('parser file to: ' + type)
const file = await parser.parse(data)
this.logger.info('finish parser')
const mimeTypes: Record<TypeParser, string> = {
'csv': 'text/csv',
'html': 'text/html',
'pdf': 'application/pdf'
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `relatorio-pii-${timestamp}.${type}`;
return {
file,
filename: filename,
type: mimeTypes[type]
}
} catch (error) {
this.logger.error(error.message);
throw error;
}
}
async createDataAsset(data: Messages.CreateDataAssetRequest, metadata) { async createDataAsset(data: Messages.CreateDataAssetRequest, metadata) {
this.logger.info('CatalogService - Manage Data assets permissions'); this.logger.info('CatalogService - Manage Data assets permissions');
if (!data.embed) data.embed = undefined; if (!data.embed) data.embed = undefined;
@@ -420,6 +476,112 @@ class CatalogService implements OnModuleInit {
); );
return res.dashboards; return res.dashboards;
} }
async createTableMetadata(body: any): Promise<number> {
const nimbusUrl = this._getNimbusUrl(body);
this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata});
const endpoint = `${nimbusUrl}/api/catalog/table-metadata/`;
this.logger.info(`Creating table metadata for table ${body.table_metadata.table_name}`, {...body.logMetadata});
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
this.logger.debug(`Payload: ${JSON.stringify(body.table_metadata)}`, {...body.logMetadata});
try {
const { data, status } = await axios.post(endpoint, {...body.table_metadata});
this.logger.info(
`Table metadata created successfully with status ${status} for table ${body.table_metadata.table_name}`,
{...body.logMetadata},
);
return data.id;
} catch (error) {
this.logger.error(
`Failed to create table metadata for table ${body.table_metadata.table_name} failed with status ${
error.response?.status
} because of ${JSON.stringify(error.response?.data) || error.message}`, {...body.logMetadata});
throw new Error(error.response?.data?.message || error.message);
}
}
async createColumnMetadata(body: any): Promise<number[]> {
const nimbusUrl = this._getNimbusUrl(body);
this.logger.info(`Nimbus URL: ${nimbusUrl}`, body.logMetadata);
const endpoint = `${nimbusUrl}/api/catalog/column-metadata/`;
try {
this.logger.info(`Creating column metadata for table ${body.column_metadata.table_name}`, {...body.logMetadata});
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
this.logger.debug(`Payload: ${JSON.stringify(body.column_metadata)}`, {...body.logMetadata});
const { data, status } = await axios.post(endpoint, body.column_metadata);
this.logger.info(
`Column metadata created successfully with status ${status} for table ${body.column_metadata.table_name}`,
{...body.logMetadata},
);
return data.map((column) => column.id);
} catch (error) {
this.logger.error(
`Failed to create column metadata failed with status for table ${body.column_metadata.table_name} ${
error.response?.status
} because of ${error.response?.data || error.message}`, {...body.logMetadata});
throw new Error(error.response?.data?.message || error.message);
}
}
async createDataPreview(body: any): Promise<number> {
const nimbusUrl = this._getNimbusUrl(body);
this.logger.info(`Nimbus URL: ${nimbusUrl}`, {...body.logMetadata});
const endpoint = `${nimbusUrl}/api/catalog/data-preview/`;
this.logger.info(`Creating data preview for table ${body.data_preview.table_name}`, {...body.logMetadata});
this.logger.info(`Using endpoint: ${endpoint}`, {...body.logMetadata});
this.logger.debug(`Payload: ${JSON.stringify(body.data_preview)}`, {...body.logMetadata});
try {
const { data, status } = await axios.post(endpoint, body.data_preview);
this.logger.info(
`Data preview created successfully with status ${status} for table ${body.data_preview.table_name}`,
{...body.logMetadata},
);
return data.id;
} catch (error) {
this.logger.error(
`Failed to create data preview for table ${body.data_preview.table_name} failed with status ${
error.response?.status
} because of ${error.response?.data || error.message}`,
{...body.logMetadata},
);
throw new Error(error.response?.data?.message || error.message);
}
}
async catalogDatasetItem(table_metadata_id: number, metadata: Metadata) {
const customer_name_raw = metadata.get('customer_name');
const customer_name = customer_name_raw?.[0]?.toString();
if (!customer_name) {
throw new BadRequestException('Customer name not found in metadata');
}
const res = await lastValueFrom(
this.platformWriteService.CatalogDataAssets(
{
data_assets: [
{
data_asset_id: table_metadata_id.toString(),
customer_name: customer_name,
data_asset_type: 'dataset',
},
],
},
metadata,
),
);
return res;
}
} }
export { CatalogService }; export { CatalogService };
+8
View File
@@ -0,0 +1,8 @@
export class PiiDto {
database_name: string;
table_schema: string;
table_name: string;
column_name: string;
data_type: string;
pii_rules: string;
}
+46 -1
View File
@@ -7,15 +7,18 @@ import {
HttpStatus, HttpStatus,
Inject, Inject,
Param, Param,
Post,
Put, Put,
Query, Query,
UseFilters, UseFilters,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiOkResponse, ApiProduces, ApiTags } from '@nestjs/swagger'; import { ApiOkResponse, ApiProduces, ApiTags } from '@nestjs/swagger';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum'; import { DADOSFERA_MODULES_KEYS, PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { import {
Authenticated, Authenticated,
RequireAllPermissions, RequireAllPermissions,
RequireModule,
RequireSomePermission,
} from 'src/decorators/authentication.decorator'; } from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter'; import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomersService } from './customers.service'; import { CustomersService } from './customers.service';
@@ -23,6 +26,7 @@ import { CustomerLinkRequest, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator'; import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms'; import type { StringValue } from 'ms';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { EnforceMfa } from './dtos/enforce-mfa';
@ApiTags('Customers') @ApiTags('Customers')
@Controller('customers') @Controller('customers')
@@ -38,6 +42,15 @@ export class CustomersController {
this.logger = dadosferaLogger.logger; this.logger = dadosferaLogger.logger;
} }
@Post(':id/mfa')
@Authenticated()
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@RequireModule(DADOSFERA_MODULES_KEYS.DANGER_ZONE)
async enableMfaEnforce(@Param('id') id: string, @Body() data: EnforceMfa) {
this.logger.info('enableMfaEnforce', { id });
return await this.customersService.enableEnforceMfa(id, data.enabled);
}
@Get(':id/links') @Get(':id/links')
@Authenticated() @Authenticated()
@ApiOkResponse({ type: CustomerLinksResponse }) @ApiOkResponse({ type: CustomerLinksResponse })
@@ -91,4 +104,36 @@ export class CustomersController {
const metadata = PackTheMetadata(user); const metadata = PackTheMetadata(user);
return this.customersService.getMonitoringDashboardUrl(metadata); return this.customersService.getMonitoringDashboardUrl(metadata);
} }
@Get('logs-dashboard')
@Authenticated()
@RequireAllPermissions(
PERMISSIONS_GROUPS.USERS.permissions.ADMIN,
)
@RequireModule(DADOSFERA_MODULES_KEYS.LOG_DASHBOARD)
async getCustomerMixPanelLogsDashboard(
@User() user: RequestUser,
): Promise<{ url: string }> {
this.logger.info('getLogsDashboardUrl');
const metadata = PackTheMetadata(user);
const result = await this.customersService.getLogsDashboardUrl(metadata);
return result;
}
@Get('access-dashboard')
@Authenticated()
@RequireAllPermissions(
PERMISSIONS_GROUPS.USERS.permissions.ADMIN,
)
@RequireModule(DADOSFERA_MODULES_KEYS.ACCESS_DASHBOARD)
async getAccessDashboard(
@User() user: RequestUser,
): Promise<{ url: string }> {
this.logger.info('getAccessDashboard');
const metadata = PackTheMetadata(user);
const result = await this.customersService.getAccessDashboardUrl(user.customer_name, metadata);
return result;
}
} }
+58 -1
View File
@@ -5,6 +5,7 @@ import {
HttpException, HttpException,
HttpStatus, HttpStatus,
InternalServerErrorException, InternalServerErrorException,
ForbiddenException,
} from '@nestjs/common'; } from '@nestjs/common';
import { firstValueFrom, lastValueFrom } from 'rxjs'; import { firstValueFrom, lastValueFrom } from 'rxjs';
@@ -33,6 +34,7 @@ import DadosferaLogger from '@dadosfera/dadosfera-logs';
// This function will accept any string, which may result in a bug. // This function will accept any string, which may result in a bug.
@Injectable() @Injectable()
export class CustomersService implements OnModuleInit { export class CustomersService implements OnModuleInit {
private customerService: CustomersProtoService; private customerService: CustomersProtoService;
private logger: DadosferaLogger; private logger: DadosferaLogger;
private pipelineReadService: ReadService.PipelineV2ReadService; private pipelineReadService: ReadService.PipelineV2ReadService;
@@ -145,7 +147,7 @@ export class CustomersService implements OnModuleInit {
const res = await lastValueFrom( const res = await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl( this.pipelineReadService.PipelineV2GetDashboardUrl(
{ {
dashboard_id: '45', dashboard_id: '98',
exp: '15m', exp: '15m',
metabase_customer_name: 'dadosferatech', metabase_customer_name: 'dadosferatech',
}, },
@@ -156,4 +158,59 @@ export class CustomersService implements OnModuleInit {
return res; return res;
} }
async getLogsDashboardUrl(metadata: Metadata) {
logger.info('CustomersService - getMixPanelLogsDashboardUrl');
const res = await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl(
{
dashboard_id: '103',
exp: '15m',
metabase_customer_name: 'dadosferatech',
},
metadata,
),
);
logger.info('Done');
return res;
}
async getAccessDashboardUrl(customerName: string, metadata: Metadata) {
/*
* TODO(Refactor): dar um jeito de exibir o dash da sbm diferente dos outros customer
* pois o signicado de department para sbm significa as instituições do usuários
*/
if (customerName !== 'sbmoffshorecom') {
throw new ForbiddenException();
}
logger.info('CustomersService - getAccessDashboardUrl');
const res = await this.getDashboardUrl('105', metadata);
logger.info('Done');
return res;
}
private async getDashboardUrl(dashboardId: string, metadata: Metadata) {
return await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl(
{
dashboard_id: dashboardId,
exp: '15m',
metabase_customer_name: 'dadosferatech',
},
metadata
)
);
}
async enableEnforceMfa(id: string, enabled: boolean) {
return await lastValueFrom(
this.customerService.CustomerUpdateEnforceMfa({
customerId: id,
enforceMfa: enabled
})
)
}
} }
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
export class EnforceMfa {
@ApiProperty()
enabled: boolean
}
+11 -3
View File
@@ -1,4 +1,4 @@
import { Body, Controller, Inject, Param, Post } from '@nestjs/common'; import { Body, Controller, Inject, Param, Post, Req } from '@nestjs/common';
import { init } from 'mixpanel'; import { init } from 'mixpanel';
import { Authenticated } from 'src/decorators/authentication.decorator'; import { Authenticated } from 'src/decorators/authentication.decorator';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator'; import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@@ -13,7 +13,12 @@ export class MixpanelController {
private readonly mixpanelToken: string, private readonly mixpanelToken: string,
) {} ) {}
@Post(':id') @Post(':id')
async trackEvent(@Param('id') id, @Body() body, @User() user: RequestUser) { async trackEvent(
@Param('id') id,
@Body() body,
@User() user: RequestUser,
@Req() request
) {
delete body.info; delete body.info;
const mixpanel = init(this.mixpanelToken); const mixpanel = init(this.mixpanelToken);
@@ -66,13 +71,16 @@ export class MixpanelController {
$email: user.username.includes('@') $email: user.username.includes('@')
? user.username ? user.username
: user.username + '@dadosfera.ai', : user.username + '@dadosfera.ai',
customer_name: user.customer_name, customer_name: user.customer_name
}); });
await mixpanel.track(id, { await mixpanel.track(id, {
distinct_id: user.username, distinct_id: user.username,
customer: user.customer_name, customer: user.customer_name,
env: process.env.ENV, env: process.env.ENV,
$ip: request.ip,
$os: request.headers['sec-ch-ua-platform'] || '',
$browser: request.headers['user-agent'],
...body, ...body,
}); });
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
export class NetworkPoliciesDTO {
@ApiProperty()
policies: string []
}
@@ -0,0 +1,64 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { ApiOkResponse } from '@nestjs/swagger';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { NetworkPoliciesDTO } from './dto/network-policy.dto';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { NetworkPolicyService } from './network-policy.service';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
@Controller('network-policy')
export class NetworkPolicyController {
constructor(private networkPolicyService: NetworkPolicyService) {}
@Get()
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
async getNetworks(
@User() user: RequestUser,
) {
return await this.networkPolicyService.getByCustomer(
user.customer_id
);
}
@Post()
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse()
@HttpCode(HttpStatus.CREATED)
async applyNetworkPolicies(
@User() user: RequestUser,
@Body() data: NetworkPoliciesDTO,
) {
const metadata = PackTheMetadata(user);
return await this.networkPolicyService.apply(
data.policies,
user.customer_id,
metadata,
);
}
@Delete()
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse()
@HttpCode(HttpStatus.OK)
async removeNetworkPolicies(
@User() user: RequestUser,
@Body() data: NetworkPoliciesDTO,
) {
const metadata = PackTheMetadata(user);
return await this.networkPolicyService.delete(
data.policies,
user.customer_id,
metadata,
);
}
}
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { NetworkPolicyController } from './network-policy.controller';
import { NetworkPolicyService } from './network-policy.service';
import { ClientsModule } from '@nestjs/microservices';
import { DucClient } from '../duc/client.config';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
const ducClient = new DucClient();
@Module({
imports: [
ClientsModule.register([
ducClient.providerOptions
]),
],
controllers: [NetworkPolicyController],
providers: [NetworkPolicyService, DadosferaLogger]
})
export class NetworkPolicyModule {}
@@ -0,0 +1,74 @@
import { Inject, Injectable } from '@nestjs/common';
import { DucClient } from '../duc/client.config';
import { ClientGrpc } from '@nestjs/microservices';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { Metadata } from '@grpc/grpc-js';
import { lastValueFrom } from 'rxjs';
import { NetworkPoliciesDTO } from './dto/network-policy.dto';
@Injectable()
export class NetworkPolicyService {
private customerService: CustomersProtoService;
private logger: DadosferaLogger;
constructor(
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.customerService = this.grpcClient.getService<CustomersProtoService>(
ProtoServices.CustomersProtoService,
);
}
async getByCustomer(id: string): Promise<NetworkPoliciesDTO> {
const {
customer
} = await lastValueFrom(this.customerService.CustomerFindOneById({
id
}));
return {
policies: customer.networkPolicies
}
}
async apply(
networkPolicies: string[],
customerId: string,
metadata: Metadata,
) {
return await lastValueFrom(
this.customerService.CustomerCreateNetworkPolicy(
{
customerId,
networkPolicies,
},
metadata,
),
);
}
async delete(
networkPolicies: string[],
customerId: string,
metadata: Metadata,
) {
return await lastValueFrom(
this.customerService.CustomerRemoveNetworkPolicy(
{
customerId,
networkPolicies,
},
metadata,
),
);
}
}
+15 -22
View File
@@ -1,5 +1,5 @@
import DadosferaLogger from '@dadosfera/dadosfera-logs'; import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { Body, Controller, Header, HttpCode, Inject, Param, Post, Query, Req, UseFilters, UseGuards } from '@nestjs/common'; import { Body, Controller, ForbiddenException, Header, Headers, HttpCode, HttpException, Inject, Param, Post, Query, Req, Res, UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
import { ApiCreatedResponse, ApiHeaders, ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { ApiCreatedResponse, ApiHeaders, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator'; import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter'; import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
@@ -8,7 +8,6 @@ import { UsersService } from '../users/users.service';
import { Language } from 'src/decorators/language.decorator'; import { Language } from 'src/decorators/language.decorator';
import { OpenDataService } from './open-data.service'; import { OpenDataService } from './open-data.service';
import { CreateUserOpenDataDTO, WordpressForm } from './dto/wordpres-form'; import { CreateUserOpenDataDTO, WordpressForm } from './dto/wordpres-form';
import { CORSGuard, SetOrigin } from 'src/decorators/set-origin.decorator';
import { Metadata } from '@grpc/grpc-js'; import { Metadata } from '@grpc/grpc-js';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { request } from 'http'; import { request } from 'http';
@@ -31,8 +30,6 @@ export class OpenDataController {
} }
@Post("/sharing-ocean-data") @Post("/sharing-ocean-data")
// @SetOrigin('devsbm.dadosfera.io')
// @UseGuards(CORSGuard)
@HttpCode(200) @HttpCode(200)
@Header('content-type', 'application/json') @Header('content-type', 'application/json')
@ApiOkResponse() @ApiOkResponse()
@@ -43,23 +40,20 @@ export class OpenDataController {
language: string, language: string,
@Req() @Req()
request: Request, request: Request,
@Headers('origin')
origin: string
) { ) {
this.logger.info('createUser for open data' this.logger.info('createUser for open data' + JSON.stringify(request.headers));
+ JSON.stringify({
origin: request.headers.origin,
language,
body
}));
// "401573bb-334f-44b2-b30e-88d4cea31ae9" const corslist = ["https://devsbm.dadosfera.io", "https://sharingoceandata.com"];
// const OPENDATA_PUBLIC_USERS_GROUP_ID = process.env.OPEN_GROUP_ID; if (!corslist.includes(origin)) {
// ""f239718a-a271-4ef9-ae7e-02a2f0f3aa6e"" this.logger.info('block request by cors list: '+ origin);
// const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID; throw new ForbiddenException();
const OPENDATA_CUSTOMER_ID = "b3e3dfe5-b992-4586-a73c-c0b0c00f615d"; }
this.logger.info("OPENDATA_CUSTOMER_ID: " + process.env.OPEN_CUSTOMER_ID) const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID;
this.logger.info("OPEN_GROUP_ID: " + process.env.OPEN_GROUP_ID) const OPENDATA_GROUP_ID = process.env.OPEN_GROUP_ID;
const roles = ["e3f98a2f-7748-4981-8505-7695c8ca8218"]; const roles = [process.env.OPEN_GROUP_ID];
const metadata = PackTheMetadata({ const metadata = PackTheMetadata({
language: language || 'en-us' language: language || 'en-us'
}); });
@@ -77,7 +71,6 @@ export class OpenDataController {
this.logger.error('user data ' + e.message); this.logger.error('user data ' + e.message);
} }
const user: CreateUserOpenDataDTO = { const user: CreateUserOpenDataDTO = {
email: data["email"], email: data["email"],
enquiryType: data["enquiry_type"], enquiryType: data["enquiry_type"],
@@ -85,11 +78,11 @@ export class OpenDataController {
lastName: data["last_name"], lastName: data["last_name"],
organization: data["organization"] organization: data["organization"]
} }
this.logger.info('user request' + JSON.stringify({ user, roles, customer: OPENDATA_CUSTOMER_ID })); this.logger.info(`user request to group ${OPENDATA_CUSTOMER_ID} with role ${OPENDATA_GROUP_ID}`);
try { try {
await this.openDataService.createUser(OPENDATA_CUSTOMER_ID, user, roles, metadata); const id = await this.openDataService.createUser(OPENDATA_CUSTOMER_ID, user, roles, metadata);
this.logger.info('user created with sucessfull data'); this.logger.info('user created with id: '+ id);
return { return {
success: true, success: true,
status: 'success', status: 'success',
+2 -2
View File
@@ -40,10 +40,10 @@ export class OpenDataService implements OnModuleInit {
} }
try { try {
await lastValueFrom( const { user } = await lastValueFrom(
this.usersClientService.SimpleUserCreate(body, metadata), this.usersClientService.SimpleUserCreate(body, metadata),
); );
return "User created"; return user.id;
} catch(err) { } catch(err) {
return err; return err;
} }
+1 -1
View File
@@ -328,7 +328,7 @@ export class PipelinesService implements OnModuleInit {
const res = await lastValueFrom( const res = await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl( this.pipelineReadService.PipelineV2GetDashboardUrl(
{ {
dashboard_id: '83', dashboard_id: '95',
exp: '15m', exp: '15m',
metabase_customer_name: 'dadosferatech', metabase_customer_name: 'dadosferatech',
}, },
+2 -2
View File
@@ -27,9 +27,9 @@ export class CustomerTheme implements Theme {
@ApiProperty() @ApiProperty()
textColor: string; textColor: string;
@ApiProperty() @ApiProperty()
logoWhite: string; logo: string;
@ApiProperty() @ApiProperty()
logoBlack: string; logoLogin: string;
} }
export class CustomerThemeResponse { export class CustomerThemeResponse {
+14 -12
View File
@@ -59,22 +59,22 @@ export class ThemeController {
id, id,
})); }));
const logoWhite = files.find(file => file.fieldname === 'logoWhite'); const logo = files.find(file => file.fieldname === 'logo');
const logoBlack = files.find(file => file.fieldname === 'logoBlack'); const logoLogin = files.find(file => file.fieldname === 'logoLogin');
this.validFileSize(logoWhite); this.validFileSize(logo);
this.validFileSize(logoBlack); this.validFileSize(logoLogin);
this.validMimeType(logoWhite); this.validMimeType(logo);
this.validMimeType(logoBlack); this.validMimeType(logoLogin);
try { try {
const theme = await this.themeService.createThemeByCustomer(id, { const theme = await this.themeService.createThemeByCustomer(id, {
...data, ...data,
logoWhite, logo,
logoBlack logoLogin
}); });
this.logger.info('saveCustomertheme' + JSON.stringify(theme)); this.logger.info('saveCustomertheme' + JSON.stringify(theme));
return { theme: theme }; return theme;
} catch (err) { } catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - saveCustomertheme - Expect CUSTOMER.NOT_FOUND'); this.logger.error('Error - saveCustomertheme - Expect CUSTOMER.NOT_FOUND');
@@ -98,7 +98,7 @@ export class ThemeController {
const mimeTypesValid = ['image/jpeg', 'image/jpg', 'image/png']; const mimeTypesValid = ['image/jpeg', 'image/jpg', 'image/png'];
if (file && !mimeTypesValid.includes(file.mimetype)) { if (file && !mimeTypesValid.includes(file.mimetype)) {
throw new HttpException(`O Arquivo ${file.fieldname} deve ser jpeg, jpg ou png`, HttpStatus.BAD_REQUEST); throw new HttpException(`O Arquivo ${file.fieldname} deve ser jpeg, jpg, ou png`, HttpStatus.BAD_REQUEST);
} }
} }
@@ -109,8 +109,10 @@ export class ThemeController {
try { try {
const data = await this.themeService.getThemeByCustomer(id); const data = await this.themeService.getThemeByCustomer(id);
this.logger.info('Success - getCustomerTheme with id'+ id); this.logger.info('Success - getCustomerTheme'+ JSON.stringify(data));
return data; if (data?.theme) return data;
return { theme: null };
}catch (err) { }catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) { if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - getCustomerTheme - Expect CUSTOMER.NOT_FOUND'); this.logger.error('Error - getCustomerTheme - Expect CUSTOMER.NOT_FOUND');
+43 -18
View File
@@ -21,11 +21,10 @@ import { resolve } from 'path';
import { Readable } from 'stream'; import { Readable } from 'stream';
type Files = { type Files = {
logoWhite: Express.Multer.File, logo: Express.Multer.File,
logoBlack: Express.Multer.File, logoLogin: Express.Multer.File,
} }
// This function will accept any string, which may result in a bug.
@Injectable() @Injectable()
export class ThemeService implements OnModuleInit { export class ThemeService implements OnModuleInit {
private themeService: ThemeProtoService; private themeService: ThemeProtoService;
@@ -63,12 +62,12 @@ export class ThemeService implements OnModuleInit {
chunk: Buffer.alloc(0) chunk: Buffer.alloc(0)
}) })
if(theme.logoBlack) { if(theme.logo) {
await this.sendFile(theme.logoBlack, customerThemeRequest$); await this.sendFile(theme.logo, customerThemeRequest$);
} }
if(theme.logoWhite) { if(theme.logoLogin) {
await this.sendFile(theme.logoWhite, customerThemeRequest$); await this.sendFile(theme.logoLogin, customerThemeRequest$);
} }
customerThemeRequest$.complete(); customerThemeRequest$.complete();
@@ -95,18 +94,20 @@ export class ThemeService implements OnModuleInit {
} }
private async sendFile(file: Express.Multer.File, stream$: ReplaySubject<ThemeRequest>) { private async sendFile(file: Express.Multer.File, stream$: ReplaySubject<ThemeRequest>) {
const bufferStream = new Readable({ const chunkSize = 4 * 1024 * 1024;
highWaterMark: 1024 * 1024, // 1 MB por chunk const bufferStream = new CustomBufferStream(file.buffer, chunkSize);
read() {} const parseMimitypeForExtension = {
}); 'image/jpeg': '.jpeg',
bufferStream.push(file.buffer); 'image/jpg': '.jpg',
bufferStream.push(null); 'image/png': '.png',
'image/svg+xml': '.svg',
}
const extension = parseMimitypeForExtension[file.mimetype];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
bufferStream.on('data', (chunk) => { bufferStream.on('data', (chunk) => {
console.log('enviando chunk', file.fieldname) const filename = file.fieldname.concat(extension);
const mimetype = file.mimetype.split('/')[1]; // example image/jpeg
const filename = file.fieldname.concat(".", mimetype);
stream$.next({ stream$.next({
customerId: '', customerId: '',
displayName: '', displayName: '',
@@ -119,15 +120,39 @@ export class ThemeService implements OnModuleInit {
}); });
bufferStream.on('end', () => { bufferStream.on('end', () => {
console.log('terminou de enviar')
resolve(file.filename) resolve(file.filename)
}); });
bufferStream.on('error', (err) => { bufferStream.on('error', (err) => {
console.error('Erro no stream:', err);
reject(err); reject(err);
}); });
}); });
} }
} }
class CustomBufferStream extends Readable {
buffer: Buffer;
offset: number;
chunkSize: number;
constructor(buffer: Buffer, chunkSize: number) {
super({ highWaterMark: chunkSize }); // Configura o tamanho do chunk
this.buffer = buffer;
this.offset = 0;
this.chunkSize = chunkSize;
}
_read() {
if (this.offset < this.buffer.length) {
const end = Math.min(this.offset + this.chunkSize, this.buffer.length);
const copiedBuf = Uint8Array.prototype.slice.call(this.buffer);
const chunk = copiedBuf.slice(this.offset, end);
this.offset = end;
this.push(chunk);
} else {
this.push(null);
}
}
}
+4
View File
@@ -11,6 +11,10 @@ interface IMetadata {
roles?: string[]; roles?: string[];
is_data_manager?: boolean; is_data_manager?: boolean;
access_token?: string; access_token?: string;
host?: string;
origin?: string;
custom_host?: string;
is_mask?: boolean;
} }
export function PackTheMetadata(info: IMetadata): Metadata { export function PackTheMetadata(info: IMetadata): Metadata {
+7
View File
@@ -77,6 +77,13 @@ export function EnrichErrorCode(code: string) {
message: 'Token para recuperar senha inválido', message: 'Token para recuperar senha inválido',
code, code,
}; };
case ErrorCodes.AUTH.REQUIRED_PASSWORD_RESET:
return {
statusCode: HttpStatus.UNAUTHORIZED,
error: 'Não permitido',
message: 'Seu usuário foi resetado por um administrador, resete a senha pelo link "Esqueci a senha", para prosseguir',
code,
};
case ErrorCodes.AUTH.WEAK_NEW_PASSWORD: case ErrorCodes.AUTH.WEAK_NEW_PASSWORD:
return { return {
+14
View File
@@ -0,0 +1,14 @@
import { stringify } from 'csv'
import { Parser } from './parser.interface';
export class CsvParser<T> implements Parser<T>{
async parse(data: T[]) {
return new Promise<string>((resolve, reject) => {
stringify(data, { header: true }, function (err, output) {
if (err) reject(err);
resolve(output)
});
})
}
}
+17
View File
@@ -0,0 +1,17 @@
import { readFileSync } from 'fs';
import Handlebars from 'handlebars';
import { Parser } from './parser.interface';
export class HtmlParser<T> implements Parser<T> {
constructor(private readonly templatePath: string) {}
async parse(data: T[]) {
const templateSource = readFileSync(this.templatePath, 'utf8');
const template = Handlebars.compile(templateSource);
return template({
dados: data,
dataGeracao: new Date().toLocaleString('pt-BR'),
});
}
}
+1
View File
@@ -0,0 +1 @@
export type TypeParser = "html" | "csv" | "pdf"
+25
View File
@@ -0,0 +1,25 @@
import { TypeParser } from './parser-types';
import { HtmlParser } from './html-parser';
import { CsvParser } from './csv-parser';
import { PDFParser } from './pdf-parser';
import { Parser } from './parser.interface';
export class ParserBuilder {
static build<T>(type: TypeParser): Parser<T> {
const templatePath = '/app/dist/assets/template/pii-reporter.html';
if (type === 'html') {
const htmlParser = new HtmlParser<T>(templatePath);
return htmlParser;
} else if (type === 'csv') {
const csvParser = new CsvParser<T>();
return csvParser;
} else if (type === 'pdf') {
const htmlParser = new HtmlParser<T>(templatePath);
const pdfParser = new PDFParser(htmlParser);
return pdfParser;
}
throw new Error('Unsupported Type: ' + type);
}
}
+3
View File
@@ -0,0 +1,3 @@
export interface Parser<T> {
parse(data: T[]): Promise<string | Buffer>
}
+57
View File
@@ -0,0 +1,57 @@
import puppeteer from 'puppeteer';
import { Parser } from './parser.interface';
import { HtmlParser } from './html-parser';
export class PDFParser<T> implements Parser<T> {
constructor(private htmlParser: HtmlParser<T>) {}
async parse(data: T[]) {
try {
const browser = await puppeteer.launch({
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
],
});
const page = await browser.newPage();
page.setDefaultNavigationTimeout(0);
const html = await this.htmlParser.parse(data);
await page.setContent(html, {
waitUntil: 'networkidle0',
timeout: 30000,
});
// Configurações adicionais para garantir um PDF válido
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: {
top: '20mm',
right: '15mm',
bottom: '20mm',
left: '15mm',
},
preferCSSPageSize: true,
displayHeaderFooter: false,
headerTemplate: '',
footerTemplate: '',
omitBackground: false,
pageRanges: '',
tagged: true,
outline: false,
timeout: 30000,
});
await browser.close();
return Buffer.from(pdf);
} catch (error) {
console.error('Error generating PDF:', error);
throw error;
}
}
}
+1
View File
@@ -13,6 +13,7 @@ export const AUTH = {
RESET_PASSWORD_CODE_EXPIRED: 'AUTH.RESET_PASSWORD_CODE_EXPIRED', RESET_PASSWORD_CODE_EXPIRED: 'AUTH.RESET_PASSWORD_CODE_EXPIRED',
RESET_PASSWORD_CODE_INVALID: 'AUTH.RESET_PASSWORD_CODE_INVALID', RESET_PASSWORD_CODE_INVALID: 'AUTH.RESET_PASSWORD_CODE_INVALID',
WEAK_NEW_PASSWORD: 'AUTH.WEAK_NEW_PASSWORD', WEAK_NEW_PASSWORD: 'AUTH.WEAK_NEW_PASSWORD',
REQUIRED_PASSWORD_RESET: 'AUTH.REQUIRED_PASSWORD_RESET'
}; };
export const ROLE = { export const ROLE = {
INVALID_ID: 'ROLE.INVALID_ID', INVALID_ID: 'ROLE.INVALID_ID',