Compare commits

...
608 Commits
Author SHA1 Message Date
marcos-silva-rodrigues 19877f1b29 FIX: merge with main 2025-07-18 16:46:19 -03:00
marcos-silva-rodrigues e9880bcf68 FIX: add logger in header 2025-07-18 15:56:23 -03:00
marcos-silva-rodrigues 4c7111dcb6 FIX: using referer headaer 2025-07-18 15:56:10 -03:00
marcos-silva-rodrigues e81cfdcce1 FIX: using only header origin 2025-07-18 15:55:56 -03:00
marcos-silva-rodrigues 3eba3f414f FIX: send redirect uri 2025-07-18 15:55:45 -03:00
marcos-silva-rodrigues fbeb8dfd91 FIX: redis env 2025-07-18 15:55:29 -03:00
marcos-silva-rodrigues dda3e8baaf FIX: add logger 2025-07-18 15:54:27 -03:00
marcos-silva-rodrigues c1e4f0b18a FIX: remove express session 2025-07-18 15:54:24 -03:00
marcos-silva-rodrigues cb66d4f6c0 FEAT: dynamic callback and issuer urls 2025-07-16 11:35:47 -03:00
marcos-silva-rodrigues 42a39ee891 FEAT: send user id to refresh token endpoint 2025-07-15 16:21:44 -03:00
marcos-silva-rodrigues c9bfe68ea5 FEAT: list identity providers link for sso 2025-07-14 15:16:19 -03:00
marcos-silva-rodrigues 8cb7144ff4 FIX: send accessToken in refresh token endpoint 2025-07-14 15:14:25 -03:00
Marcos Rodrigues Silva 68cc58e2fe Merge pull request #326 from dadosfera/release/11-07
Release/11 07
2025-07-14 10:54:14 -03:00
marcos-silva-rodrigues cbe5556ac9 FIX: auth callback 2025-07-14 10:51:36 -03:00
marcos-silva-rodrigues eb80967608 FIX: tests 2025-07-11 11:36:23 -03:00
marcos-silva-rodrigues 66fea0722d FEAT: enabled network check in app.dadosfera 2025-07-11 11:12:35 -03:00
marcos-silva-rodrigues c917061b97 Merge branch 'feature/export-users-and-assets' into release/11-07 2025-07-11 11:04:31 -03:00
Marcos Rodrigues Silva ab280ee152 Merge pull request #324 from dadosfera/chore/update-unimed-ips
CHORE: Update unimed ips
2025-07-08 09:22:26 -03:00
marcos-silva-rodrigues 16c8137160 CHORE: Update unimed ips 2025-07-07 17:26:26 -03:00
marcos-silva-rodrigues c98a5ff896 FEAT: endpoint to dowload assets list 2025-07-05 11:21:05 -03:00
marcos-silva-rodrigues d68e436a8b FEAT: export users in csv 2025-07-05 09:38:16 -03:00
marcos-silva-rodrigues d3040cd5a8 FEAT: update and delete identity provider 2025-07-03 10:59:43 -03:00
marcos-silva-rodrigues f726a9883f FEAT: create user from identity provider 2025-06-27 13:21:59 -03:00
Marcos Rodrigues Silva c18a7baa37 Merge pull request #322 from dadosfera/chore/add-dadosfera-vpn-unimed-chart
FIX: add dadosfera vpn ip in unimed chart
2025-06-25 11:12:33 -03:00
marcos-silva-rodrigues d638d518f8 FIX: change unimed chart name 2025-06-25 11:06:41 -03:00
marcos-silva-rodrigues bdd371ffe8 FIX: add dadosfera vpn ip in unimed chart 2025-06-25 10:58:18 -03:00
marcos-silva-rodrigues 57e6e49c7c FEAT: init url callback 2025-06-25 10:28:26 -03:00
marcos-silva-rodrigues a5c8de496d FEAT: map permission to identity provider 2025-06-24 16:20:03 -03:00
Marcos Rodrigues Silva d478f32760 Merge pull request #321 from dadosfera/hotfix/nginx-timeout
FIX: add nginx proxy timeout 5 min
2025-06-24 11:02:29 -03:00
marcos-silva-rodrigues 5d8e908339 FIX: add nginx proxy timeout 5 min 2025-06-24 10:26:50 -03:00
Marcos Rodrigues Silva 606bae016b Merge pull request #320 from dadosfera/hotfix/maesto-unimed-hostname
FIX: correct helm value
2025-06-23 18:43:58 -03:00
marcos-silva-rodrigues 6910a2f04c FIX: correct helm value 2025-06-23 18:38:20 -03:00
Marcos Rodrigues Silva 8bee1788c1 Merge pull request #319 from dadosfera/hotfix/maesto-unimed-hostname
FIX: maestro custom hostname to unimed
2025-06-23 18:04:43 -03:00
marcos-silva-rodrigues b10459cb15 FIX: maestro custom hostname to unimed 2025-06-23 17:51:14 -03:00
Marcos Rodrigues Silva 3b48dd1613 Merge pull request #317 from dadosfera/release/2025-06
Release/2025 06
2025-06-23 17:07:55 -03:00
marcos-silva-rodrigues bac74c9577 FIX: default value and throw correct Error Class 2025-06-23 15:36:26 -03:00
marcos-silva-rodrigues 19d748ae09 CHORE: add restricted ip and unimed chart 2025-06-23 14:47:39 -03:00
marcos-silva-rodrigues 4db865371f Merge branch 'bugfix/update-pupperter-timeout' into release/2025-06 2025-06-23 14:12:25 -03:00
marcos-silva-rodrigues 75e8b3bf4b FIX: correct dto name 2025-06-17 14:58:35 -03:00
marcos-silva-rodrigues 0aa9c065a5 FEAT: block user from login when maestro is dedicated 2025-06-16 10:34:11 -03:00
marcos-silva-rodrigues cb98099a91 FIX: update timeout 2025-06-13 15:07:50 -03:00
marcos-silva-rodrigues 17de31f1a6 FIX: send env DEDICATED_PROXY 2025-06-12 14:45:13 -03:00
marcos-silva-rodrigues 9e597fadba FIX: open data ingress route 2025-06-12 14:04:45 -03:00
marcos-silva-rodrigues 41842f7c6a FIX: chart private maestro 2025-06-12 13:56:57 -03:00
marcos-silva-rodrigues 7f9755493a FIX: experimental hostname 2025-06-12 12:29:51 -03:00
marcos-silva-rodrigues fea587ad36 FEAT: control login by dedicated customer id injected 2025-06-12 11:51:56 -03:00
Marcos Rodrigues Silva b2b7537fee Merge pull request #308 from dadosfera/fix/wordpress-block-list
FIX: comment block list
2025-06-10 15:25:27 -03:00
marcos-silva-rodrigues 01263017e7 FIX: comment block list 2025-06-10 14:24:26 -03:00
marcos-silva-rodrigues d5141e5b6a REFACTOR: rename openid to identity provider 2025-05-26 13:37:25 -03:00
Rafael Santana 8aded98a11 Merge pull request #304 from dadosfera/beta
Beta
2025-05-26 10:51:13 -03:00
Rafael Santana 0b5a0d0d7e Merge pull request #303 from dadosfera/adjusting-nestjs-payload
Adjusting nestjs payload
2025-05-26 10:40:43 -03:00
Rafael 5a36762544 UPDATE: route /catalog/register-dataset will have an limit of 10mb 2025-05-26 10:24:48 -03:00
marcos-silva-rodrigues a4b7573ce7 FIX: sync with main 2025-05-23 14:07:05 -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-silva-rodrigues 4c94a51a9d REFACTOR: openid module 2025-04-03 07:46:16 -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-silva-rodrigues f1345472b5 UPDATE: add routes to login with idp 2025-03-25 09:18:27 -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 ea9c0502b7 UPDATE: new route to add identity provider 2025-03-24 07:58:26 -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
Rafael 10c4ed179b UPDATE: add toleration for spot instances 2025-01-22 11:24:01 -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 e55a1cb657 CI: removed deploy-k8s workflow 2025-01-20 18:47:44 -03:00
Rafael 3850d4b8ae UPDATE: blank commit to force deployment 2025-01-20 18:47:13 -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
Rafael 2a6677e4d9 UPDATE: decrease the number of replicas for stg environment 2025-01-20 18:41:01 -03:00
Marcos Rodrigues Silva 74e62c001d Merge pull request #239 from dadosfera/feature/white-label-app
FIX: env fixed and add logger for the process.env
2025-01-17 08:40:08 -03:00
marcos-silva-rodrigues 8237160c6d FIX: env fixed and add logger for the process.env 2025-01-17 08:38:03 -03:00
Marcos Rodrigues Silva 41bf9d4856 Merge pull request #238 from dadosfera/feature/white-label-app
FIX: change env for stg
2025-01-16 18:47:21 -03:00
marcos-silva-rodrigues 44dcf1b281 FIX: change env for stg 2025-01-16 18:43:15 -03:00
Marcos Rodrigues Silva 104787bce8 Merge pull request #237 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-15 11:04:20 -03:00
marcos-silva-rodrigues 38a330e578 FIX: change stg env for prd env 2025-01-15 10:55:08 -03:00
marcos-silva-rodrigues 7d0b8755c9 FEAT: image upload in theme 2025-01-13 17:35:56 -03:00
Marcos Rodrigues Silva 7aadf4d9d1 Merge pull request #236 from dadosfera/feature/white-label-app
FIX: add header content type and change status code to 200
2025-01-09 08:40:59 -03:00
marcos-silva-rodrigues af8fa72377 FIX: add header content type and change status code to 200 2025-01-09 08:40:02 -03:00
Marcos Rodrigues Silva c51f262474 Merge pull request #235 from dadosfera/feature/white-label-app
FIX:  param to language and message in json
2025-01-09 08:15:46 -03:00
marcos-silva-rodrigues 54d19a78ab FIX: param to language and message in json 2025-01-09 08:13:06 -03:00
Marcos Rodrigues Silva 66f33a2246 Merge pull request #234 from dadosfera/feature/white-label-app
FIX: remove language param
2025-01-08 18:15:41 -03:00
marcos-silva-rodrigues 55d9441d10 FIX: remove language param 2025-01-08 18:12:04 -03:00
Marcos Rodrigues Silva 8b7342652e Merge pull request #233 from dadosfera/feature/white-label-app
FEAT: add language by query
2025-01-08 15:34:00 -03:00
marcos-silva-rodrigues 118c2653a5 FEAT: add language by query 2025-01-08 15:26:24 -03:00
Marcos Rodrigues Silva f7d0128d99 Merge pull request #232 from dadosfera/feature/white-label-app
FIX: remove origin decorator
2025-01-06 16:23:46 -03:00
marcos-silva-rodrigues 12cc555491 FIX: remove origin decorator 2025-01-06 16:22:03 -03:00
Marcos Rodrigues Silva cf5e36db00 Merge pull request #231 from dadosfera/feature/white-label-app
Feature/white label app
2025-01-06 15:38:40 -03:00
marcos-silva-rodrigues 7c0b87ffc3 FEAT: decorators to apply an origin in endpoint router 2025-01-06 15:25:28 -03:00
marcos-silva-rodrigues beb086ad3c FIX: fixed customer and role id 2025-01-06 14:29:22 -03:00
marcos-silva-rodrigues 8c12291096 FIX: try catch in customer controller 2025-01-06 14:15:29 -03:00
Marcos Rodrigues Silva 56a063b644 Merge pull request #230 from dadosfera/feature/white-label-app
Feature/white label app
2024-12-26 16:01:47 -03:00
marcos-silva-rodrigues 3a6054c1c6 FIX: merge conflicts 2024-12-26 15:33:53 -03:00
marcos-silva-rodrigues 3a696ecb0e CHORE: update envs 2024-12-26 15:31:22 -03:00
Rafael Santana 1166073b1c Merge pull request #229 from dadosfera/stg.dadosfera.ai
UPDATE: updating stg endpoints
2024-12-23 12:20:45 -03:00
Rafael 3026cd0748 UPDATE: updating stg endpoints 2024-12-23 12:18:42 -03:00
Marcos Rodrigues Silva b48d3ae667 Merge pull request #228 from dadosfera/feature/white-label-app
Feature/white label app
2024-12-18 17:05:34 -03:00
marcos-silva-rodrigues 5868f21f22 FEAT: merge 2024-12-18 16:25:56 -03:00
Rafael Santana 873f7ea314 Merge pull request #225 from dadosfera/helm-chart
UPDATE: create helm chart for maestro
2024-12-18 16:07:39 -03:00
marcos-silva-rodrigues 2b01b340e4 FIX: add env 2024-12-18 14:19:17 -03:00
marcos-silva-rodrigues 5888088486 FIX: remove unused suite test 2024-12-18 14:19:00 -03:00
marcos-silva-rodrigues 9e2de28032 FIX: map request body to the form wordpress 2024-12-18 13:58:08 -03:00
marcos-silva-rodrigues db2ea09d25 FEAT: create new route to open data iniciative 2024-12-12 13:54:18 -03:00
Rafael Santana 8c6b514b7b Merge pull request #227 from dadosfera/helm-chart
Helm chart
2024-12-09 14:07:01 -03:00
Rafael ad03d663bb CI: updating CI to use beta branch 2024-12-09 14:00:56 -03:00
marcos-silva-rodrigues 48d4cbc41a CHORE: stable version lib protospack 2024-12-05 10:29:06 -03:00
marcos-silva-rodrigues ae81816aa7 FEAT: create get and post route to theme 2024-12-04 15:29:51 -03:00
marcos-silva-rodrigues ccdff58056 FEAT: update protospack lib 2024-12-04 15:28:57 -03:00
Rafael c10f85950a UPDATE: Commented out the aws beanstalk deployment 2024-10-12 13:28:06 -03:00
Rafael 7d69220461 UPDATE: create helm chart for maestro 2024-10-12 13:19:57 -03:00
Gabriel Amorim e316bd1a7b Merge pull request #224 from dadosfera/feat/dev-docker
FEAT: dev docker
2024-08-16 20:31:13 -03:00
Gabriel Rosa fd47a746af FIX: microservices urls 2024-08-16 22:19:11 +00:00
Gabriel Rosa 5038c7845f FEAT: dev docker 2024-08-16 21:59:23 +00:00
Gabriel Amorim 9876eab521 Merge pull request #223 from dadosfera/feat/rls
Feat/rls
2024-08-16 17:16:18 -03:00
Gabriel Rosa 99a52c3ff2 FEAT: new protospack version 2024-08-16 17:05:38 -03:00
Gabriel Rosa 0119cee3c6 FIX: removed unused imports 2024-08-14 11:29:14 -03:00
Gabriel Rosa c7e850cc79 FEAT: rls rules routes 2024-08-14 11:13:19 -03:00
Gabriel Rosa 5da47e5438 FEAT: create RLS on pi-factory 2024-08-07 18:16:20 -03:00
Gabriel Rosa b0f9f5c77e [skip ci] DOC: improving documentation in portuguese 2024-08-05 17:57:06 -03:00
Gabriel Amorim 35f35ddeb3 Merge pull request #222 from dadosfera/SOLENG-4628-feat/restrict-asset-options
[SOLENG-4628] feat/restrict asset options
2024-07-10 12:03:32 -03:00
Gabriel Rosa 90155a8811 FIX: cahnged self-hosted actions runner to run test 2024-07-10 10:43:40 -03:00
Gabriel Rosa ae8baca694 Merge branch 'main' into SOLENG-4628-feat/restrict-asset-options 2024-07-08 09:39:53 -03:00
Gabriel Amorim 82b8751f65 Merge pull request #221 from dadosfera/fix/docker-compose
FIX: use docker compose instead of docker-compose
2024-06-28 16:58:18 -03:00
Gabriel Rosa 78ee64fc45 FIX: login aws on test 2024-06-28 16:55:31 -03:00
Gabriel Rosa 2f30837fed FIX: use docker compose instead of docker-compose 2024-06-28 16:47:45 -03:00
Gabriel Amorim f46b1b62db Merge pull request #220 from dadosfera/feat/new-customer-monitoring
FEAT: new customer monitoring dashboard
2024-06-28 16:37:38 -03:00
Gabriel Rosa 8b6cd8a88e FEAT: customer monitoring dashboard 2024-06-21 09:21:27 -03:00
Gabriel Rosa 0c333b476a FEAT: new customer monitoring 2024-06-20 10:50:07 -03:00
Gabriel Rosa 1aa9371245 FIX: Preserve slash escape 2024-05-08 18:09:56 -03:00
Gabriel Rosa 14fcf4a0a1 CI: format the node_exporter url with format() 2024-05-08 18:01:44 -03:00
Gabriel Rosa 24f824bcd1 FIX: update protospack version 2024-05-08 17:17:55 -03:00
Gabriel Rosa fcdd0bf571 Merge branch 'main' into SOLENG-4628-feat/restrict-asset-options 2024-05-08 15:37:45 -03:00
Gabriel Amorim c416a2cb97 Merge pull request #219 from dadosfera/feat/pipeline-monitoring-dashboard
Feat: pipeline monitoring dashboard
2024-05-06 16:52:26 -03:00
Gabriel Rosa d03021fc9d new protospack release 2024-05-06 15:28:26 -03:00
Gabriel Rosa 574cf48726 FIX: npm audit fix 2024-05-02 12:11:46 -03:00
Gabriel Rosa 7cef404acf FEAT: get monitoring dashboard url 2024-05-02 12:11:35 -03:00
Gabriel Rosa 3c32c3c7d4 CI: Update actions/checkout to v4 2024-04-22 14:57:01 -03:00
Gabriel Amorim d44531b963 Merge pull request #218 from dadosfera/feat/choose-deploy-dockerhub
[ TECN-6603 ] CI: update action versions and choose wether to upload to dockerhub
2024-01-10 10:05:47 -03:00
Gabriel Rosa 36c80b480e CI: update action versions and choose wether to upload to dockerhub 2024-01-04 14:58:47 -03:00
Rafael Santana eeef97679c Merge pull request #217 from dadosfera/ci/dockerhub
UPDATE: updating maestro to deploy image to dockerhub
2023-11-26 21:18:32 -03:00
Rafael Santana c789dae3f6 UPDATE: updating maestro to deploy image to dockerhub 2023-11-26 20:50:10 -03:00
Gabriel Amorim 4b20388d45 DOCS: update some routes swagger 2023-09-20 09:39:43 -03:00
Gabriel Amorim 33fd2bd35f FIX: update old protospack library 2023-09-20 09:39:27 -03:00
Gabriel Amorim 654c837d3f FEAT: new node version 2023-09-18 11:34:23 -03:00
Gabriel Amorim 416b58080d CI: fixed conventional-changelog-eslint version 2023-09-18 11:33:09 -03:00
Allan Sene 216defb76f Updated file 2023-06-11 22:43:43 -03:00
Gabriel Amorim 0970c748e0 Merge pull request #216 from dadosfera/feat/generate-token
Feat: generate token
2023-05-17 10:03:00 -03:00
Gabriel Amorim aabd741cad FEAT: new Customer Get Token Route 2023-05-16 09:05:05 -03:00
Gabriel Amorim 950c6414b6 FIX: update to new nestjs version 2023-05-12 08:28:33 -03:00
Gabriel Amorim d1149a4d52 Merge pull request #215 from dadosfera/alpha
FIX: permissions.enum.ts
2023-05-08 16:02:33 -03:00
Gabriel Amorim 4c6f4bfd84 FIX: renamed reference to intelligence permission 2023-05-08 15:56:52 -03:00
Gabriel Amorim 9caf3dc67f FIX: renamed reference to intelligence permission 2023-05-08 15:48:46 -03:00
Beatriz Antunes 635c4da48c Update permissions.enum.ts 2023-05-08 15:44:05 -03:00
Gabriel Amorim 44bb205483 Merge pull request #214 from dadosfera/fix/get-links-permission
FIX: remove permission check to get customer links
2023-05-05 13:52:38 -03:00
Gabriel Amorim edb4622684 FIX: remove permission to get customer links 2023-05-05 12:13:55 -03:00
Victor Radael abb46d78da Merge pull request #212 from dadosfera/feature/customer-links
SOLENG-1481 - Customer links endpoints
2023-04-17 17:01:41 -03:00
Victor Radael 04fdc0368a Merge pull request #213 from dadosfera/update/hubspot-tables
UPDATE: HubspotTables
2023-04-15 14:49:47 -03:00
Victor Radael 50d76622c5 UPDATE: HubspotTables 2023-04-15 14:45:18 -03:00
Victor Radael 238f56cb76 Merge branch 'SOLENG-1736' into feature/customer-links 2023-04-14 14:44:57 -03:00
Victor Radael e721d6f5f5 Merge branch 'main' into feature/customer-links 2023-04-14 13:30:57 -03:00
Victor Radael 44df13e820 UPDATE: Att protospack-v2 version 2023-04-14 13:13:47 -03:00
Victor Radael 1fd0f79dd5 Att hubspot connection 2023-04-13 15:41:44 -03:00
Victor Radael 1b92371737 Att hubspot connection 2023-04-13 14:55:33 -03:00
Colombo bf5b3484a1 FEAT: add controller method interfaces 2023-04-11 20:39:47 -03:00
Colombo eb1e248d4b FEAT: add customer (links) endpoint 2023-04-11 17:04:40 -03:00
Colombo 0827414051 FEAT: add new dto property 2023-04-10 19:36:36 -03:00
Colombo 081986607b CHORE: change protospack package to beta 2023-04-06 17:07:23 -03:00
Gabriel Amorim 189080199a Merge pull request #211 from dadosfera/feat/trigger-catalog-task
Feat/trigger catalog task
2023-03-17 12:14:15 -03:00
Gabriel Amorim 55281e3965 FIX: docs 2023-03-16 17:58:11 -03:00
Gabriel Amorim db8a6175e8 new protospack version 2023-03-16 17:45:15 -03:00
Gabriel Amorim 7b0d4dd5bc FIX: set route params type 2023-03-16 17:45:03 -03:00
Gabriel Amorim 6fd277ef3a docs 2023-03-14 16:02:22 -03:00
Gabriel Amorim 7b25bfdb69 FIX: add table_schema to request 2023-03-14 16:00:57 -03:00
Gabriel Amorim 2abc19c589 FEAT: trigger and get dataset cataloging task 2023-03-14 13:39:21 -03:00
Gabriel Amorim faf6b1df75 Merge pull request #210 from dadosfera/fix/delete-connector
Fix/delete connector
2023-03-09 18:14:14 -03:00
Gabriel Amorim 422be4a422 swaggers 2023-03-06 12:11:13 -03:00
Gabriel Amorim da136e0ef0 FIX: better swagger generation strategy (Only PRD server on external docs) 2023-03-06 12:10:12 -03:00
Gabriel Amorim 737cc9b8ea FIX: removed wrong JSON.parse on connector delete response 2023-03-06 12:09:29 -03:00
Gabriel Amorim 23f6b179fd Merge pull request #209 from dadosfera/feat/google-sheets-link
Feat/google sheets link
2023-02-27 16:03:52 -03:00
Gabriel Amorim 03e451c21d Merge branch 'main' into feat/google-sheets-link 2023-02-27 11:26:03 -03:00
Gabriel Amorim be061a0fb9 Merge pull request #208 from dadosfera/fix/upload-files
FIX: create pipeline from any file type on upload
2023-02-24 17:10:08 -03:00
Gabriel Amorim c83f6a68d1 update protospack version 2023-02-24 16:50:29 -03:00
Gabriel Amorim 2646e3e4f6 FEAT: return google sheets entire link 2023-02-24 15:58:06 -03:00
Gabriel Amorim 06424d6bcf FIX: docs 2023-02-24 14:47:49 -03:00
Gabriel Amorim 45b82515fc FIX: do not return config_controls that the pipeline don't have 2023-02-24 14:31:50 -03:00
Gabriel Amorim a9cc951e34 FIX: new error codes 2023-02-23 17:14:47 -03:00
Gabriel Amorim 93ccf506fb FIX: csv import with header=false 2023-02-22 15:13:36 -03:00
Gabriel Amorim 706d0ccfa0 FIX: get right image url 2023-02-22 14:32:41 -03:00
Beatriz Antunes c2eec4e95c Merge pull request #207 from dadosfera/fix/hubspot-chat-permission
FIX: change permission to access support chat
2023-02-22 13:10:54 -03:00
Gabriel Amorim ac3132b6eb FIX: create pipeline from any file type on upload 2023-02-22 10:18:28 -03:00
Gabriel Amorim ee8c5e7a93 Merge pull request #206 from dadosfera/feat/docs-refactor
Feat: docs refactor
2023-02-17 14:36:19 -03:00
Gabriel Rosa a2c1f2da6f FIX: new zendesk permission 2023-02-17 14:33:14 -03:00
Gabriel Rosa 7ecb50315f Update action 2023-02-17 11:01:29 -03:00
Gabriel Rosa 84b3370826 Update action 2023-02-17 11:01:08 -03:00
Gabriel Rosa aa9e0536a7 FIX: tests 2023-02-17 09:55:10 -03:00
Gabriel Rosa 544d13fac5 FIX: readme improved 2023-02-16 17:01:49 -03:00
Gabriel Rosa a4ff07a285 FIX: removed connection test from external docs 2023-02-16 12:07:47 -03:00
Gabriel Rosa 28578d3aa8 FIX: better query params on catalog get all 2023-02-15 16:46:32 -03:00
Gabriel Rosa b8ea729a33 FIX: add external docs to docsfera 2023-02-15 14:16:28 -03:00
Gabriel Rosa b383070b68 Internal docs 2023-02-15 14:07:26 -03:00
Gabriel Rosa 87882b5afc FIX: add multiple servers 2023-02-15 13:14:53 -03:00
Gabriel Rosa 6a14c2b459 FIX: hide internal endpoints 2023-02-15 13:13:43 -03:00
Gabriel Rosa f0bfb6ba8e FIX: new semantic release 2023-02-14 16:57:45 -03:00
Gabriel Rosa 99288fa805 FIX: new sematic release version 2023-02-14 16:55:39 -03:00
Gabriel Rosa 35cc0094b3 FIX: new sematic release 2023-02-14 16:51:53 -03:00
Gabriel Rosa 98262bcba9 FIX: Run semantic release on manual deploy 2023-02-14 16:48:46 -03:00
Gabriel Rosa a2c9a462e4 Run semantic release on manual deploy 2023-02-14 16:46:54 -03:00
Gabriel Rosa c5c089e5d5 Better error handling and documentation on refresh token 2023-02-14 09:25:14 -03:00
Gabriel Rosa de06512249 FIX: changed language strategy to use new custom decorator 2023-02-14 08:56:23 -03:00
Gabriel Rosa 9058b80383 Merge branch 'main' into feat/docs-refactor 2023-02-13 12:16:12 -03:00
Gabriel Rosa be64c582e1 Inject permissions more safe 2023-02-13 12:03:28 -03:00
Gabriel Rosa d6b3e02890 FIX: automattically skip ssl when microservices connection are local 2023-02-13 10:30:50 -03:00
Gabriel Amorim d06910515b FIX: force deploy 2023-02-10 15:53:10 -03:00
Gabriel Amorim d1b328d481 Merge pull request #204 from dadosfera/feat/oauth-login
Feat/oauth login
2023-02-10 14:58:20 -03:00
Gabriel Rosa fbc0d2eede FIX: moved decorators to a separate folder 2023-02-10 14:23:27 -03:00
Gabriel Rosa 945955a71c Merge branch 'main' into feat/oauth-login 2023-02-09 15:21:05 -03:00
Gabriel Rosa 9fcbeda470 FIX: deprecating old pipeline routes 2023-02-08 18:07:06 -03:00
Gabriel Rosa 8043635b1c FIX: add authentication to swagger 2023-02-08 15:23:49 -03:00
Gabriel Amorim cdba41dffa Merge pull request #203 from dadosfera/force-deploy
FIX: force deploy
2023-02-03 17:24:13 -03:00
Gabriel Rosa 5020e8913e FIX: force deploy 2023-02-03 17:21:53 -03:00
Gabriel Amorim 89369270b9 Merge pull request #202 from dadosfera/feat/embed-private
Feat/embed private
2023-02-03 17:14:14 -03:00
Gabriel Rosa 52f17725af new protospack version 2023-02-03 15:03:51 -03:00
Gabriel Rosa 6da213aebc starting module strategy 2023-02-03 11:14:15 -03:00
Gabriel Rosa a353a1b45c FIX: new permission 2023-02-02 14:00:24 -03:00
Gabriel Rosa b36ff624b5 FIX: add logic for fetching shared data_assets 2023-02-01 18:00:36 -03:00
Gabriel Rosa 03c09a525b DOCS: documenting oauth flow 2023-02-01 10:07:45 -03:00
Gabriel Amorim 9ea610a405 Merge pull request #201 from dadosfera/fix/oauth-facebook
FIX: add all scopes to facebook oauth
2023-01-31 16:08:15 -03:00
Gabriel Rosa cd97bcbac0 FIX: add all scopes to facebook oauth 2023-01-31 16:04:57 -03:00
Gabriel Rosa 52bf2bdef3 teste new docsfera cloud function url 2023-01-31 09:34:34 -03:00
Gabriel Rosa 161d1fa05d Added some logs 2023-01-30 16:35:05 -03:00
Gabriel Rosa 642bf462ad FIX: add fallback language to pt-br 2023-01-26 17:00:27 -03:00
Gabriel Rosa 6d61d74d0c FIX: set google callback 2023-01-25 09:51:22 -03:00
Gabriel Rosa 2bc060b13d FEAT: oauth sign in 2023-01-24 19:19:00 -03:00
Anderson Feitosa 28efdf95d9 Merge pull request #200 from dadosfera/feature/upload-to-s3-presigned
feature/upload-to-s3-presigned
2023-01-24 06:39:49 -03:00
Anderson Feitosa 1ef9fd4298 FEAT: update packages 2023-01-23 13:56:51 -03:00
Anderson Feitosa 5c24ec5560 FEAT: fix upload id 2023-01-20 10:05:36 -03:00
Anderson Feitosa 962e094a65 FEAT: add upload init multi part upload 2023-01-19 17:17:33 -03:00
Anderson Feitosa 131b166d8d REFACTOR: get data from body 2023-01-16 10:56:03 -03:00
Anderson Feitosa 5419f3690c FEAT: update endpoint to get signed url to upload csv file 2023-01-16 10:55:27 -03:00
Gabriel Amorim 6f3eb7965b Merge pull request #199 from dadosfera/fix/can-untoggle
FIX: canUntoggle logic
2023-01-12 18:10:23 -03:00
Gabriel Rosa 909f0d4a4e new protospack version 2023-01-12 17:59:02 -03:00
Gabriel Rosa 8c8fdf5f88 FIX: canUntoggle logic 2023-01-12 16:47:43 -03:00
Anderson Feitosa e9582a51c2 Merge pull request #198 from dadosfera/feature/mixpanel-first-name
feature/mixpanel-first-name
2023-01-12 12:28:24 -03:00
Anderson Feitosa 0590c20af8 FEAT: add more prefixed values 2023-01-12 12:03:04 -03:00
Anderson Feitosa 05eefe866b FEAT: add suffix on email 2023-01-12 12:02:16 -03:00
Anderson Feitosa fa023756c7 FEAT: replace dadosfera with empty string 2023-01-12 11:56:47 -03:00
Anderson Feitosa 7f967e5e75 FEAT: update hardcoded separator 2023-01-12 11:52:51 -03:00
Anderson Feitosa 2a0b17e45a FEAT: add more prefixed values 2023-01-12 11:49:32 -03:00
Anderson Feitosa b1f6d765b2 FEAT: add more prefixed values 2023-01-12 11:47:04 -03:00
Anderson Feitosa 3c9ed08515 FEAT: split email into fist name and last name 2023-01-12 11:40:14 -03:00
Gabriel Amorim c7ca8297c9 Merge pull request #197 from dadosfera/fix/reset-password-email
FIX: send language on reset password email
2023-01-10 20:29:58 -03:00
Gabriel Rosa d269e9efe4 FIX: send language on reset password email 2023-01-10 18:50:32 -03:00
Gabriel Amorim 61cc238891 Merge pull request #196 from dadosfera/feat/cognito-mailing
FIX: send language on create users and resend invite
2023-01-10 18:06:06 -03:00
Gabriel Rosa b5df916511 Merge branch 'main' into feat/cognito-mailing 2023-01-10 15:05:18 -03:00
Gabriel Amorim 95da106fb8 Merge pull request #195 from dadosfera/fix/customer-schedule-limit
fix/customer-schedule-limit
2023-01-06 18:29:57 -03:00
Gabriel Rosa 5730402312 update protospack 2023-01-06 18:04:16 -03:00
Gabriel Rosa c2ea8b69da FIX: customer scheduleLimit on login 2023-01-06 15:24:01 -03:00
Gabriel Rosa 14081bb9ca FIX: send language on create users and resend invite 2023-01-06 15:18:49 -03:00
Victor Radael 82d9024f24 Merge pull request #193 from dadosfera/feat/update-connection
Feat/update connection
2022-12-27 17:21:25 -03:00
Gabriel Rosa 53a6d3957a Merge branch 'main' into feat/update-connection 2022-12-27 17:01:32 -03:00
Anderson Feitosa 7dab555009 Merge pull request #194 from dadosfera/feature/download-logs
feature/download-logs
2022-12-27 16:41:11 -03:00
Anderson Feitosa 27dacf1257 FEAT: update protospack version 2022-12-27 12:24:15 -03:00
Anderson Feitosa f71bb523ff FEAT: add download logs api route 2022-12-27 12:23:03 -03:00
Victor Radael db55a8abde FEAT: Update OAuth Connection 2022-12-26 17:47:30 -03:00
Victor Radael 8f40b159d6 FEAT: MERGE main to deploy 2022-12-26 17:43:31 -03:00
Anderson Feitosa 5c6f36a760 Merge pull request #192 from dadosfera/feature/pipeline-object-to-data-asset
feature/pipeline-object-to-data-asset
2022-12-23 14:07:33 -03:00
Anderson Feitosa db4a2d9d2e FEAT: update protospack version 2022-12-22 17:43:54 -03:00
Anderson Feitosa 9a1b3e0bf4 FEAT: update protospack 2022-12-22 17:38:58 -03:00
Gabriel Rosa 67d9b3a06e Send metabaseUserId undefined to user update 2022-12-22 17:14:51 -03:00
Anderson Feitosa 1150d21763 FEAT: remove metabase user id 2022-12-22 17:02:03 -03:00
Anderson Feitosa b38e9f26c3 FEAT: add route to get data asset by pipeline and object 2022-12-22 17:00:56 -03:00
Anderson Feitosa 5a23923e53 DOCS: docsfera 2022-12-22 13:16:56 -03:00
Anderson Feitosa 3db7e99d9e FEAT: parse objects 2022-12-22 13:16:44 -03:00
Anderson Feitosa c85cd37f3a FEAT: add new api route to get pipeline objects 2022-12-22 13:08:08 -03:00
Victor Radael 190a63f0eb FEAT: Update OAuth Connection 2022-12-20 17:01:00 -03:00
Gabriel Amorim 0fc8c427a2 Merge pull request #191 from dadosfera/fix/refresh-token
Fix/refresh token
2022-12-16 09:40:56 -03:00
Gabriel Rosa 59c3ce7ed9 FIX: do not use User() on non-authenticated routes 2022-12-16 08:09:20 -03:00
Victor Radael c5c428d3d4 FIX: Refresh token 2022-12-15 19:57:52 -03:00
Victor Radael cda7f9a9b5 FEAT: Edit connection 2022-12-15 16:01:24 -03:00
Victor Radael bb3b71d5f0 Merge remote-tracking branch 'origin' into feat/upload-csv 2022-12-15 16:01:02 -03:00
Victor Radael e47459eb9a FEAT: Edit connection 2022-12-15 16:00:54 -03:00
Gabriel Amorim a2de729e0e Merge pull request #190 from dadosfera/feat/cognito-app-client
Feat/cognito app client
2022-12-14 13:20:29 -03:00
Gabriel Rosa e2cb02e51a new protospack version 2022-12-14 10:49:33 -03:00
Gabriel Rosa 8a0c91eca5 new protospack version 2022-12-14 10:02:02 -03:00
Gabriel Rosa 15302d80bc Merge branch 'main' into feat/cognito-app-client 2022-12-13 16:12:19 -03:00
Gabriel Rosa 5aa63154c5 new protospack version 2022-12-13 15:34:57 -03:00
Gabriel Rosa 48af897e79 FEAT: send customer_name on refresh token 2022-12-13 09:28:42 -03:00
Victor Radael 9eb61a0ac9 Merge pull request #189 from dadosfera/feat/upload-csv
FIX: Upload CSV
2022-12-12 17:41:44 -03:00
Victor Radael 4a9e5b0ffc FIX: Upload CSV 2022-12-12 17:04:16 -03:00
Victor Radael 23fe522cc1 Merge pull request #188 from dadosfera/feat/upload-csv
Feat/upload csv
2022-12-09 17:14:36 -03:00
Victor Radael ea8dfa2e12 FEAT: Deploy Merge 2022-12-09 17:10:50 -03:00
Anderson Feitosa 9477223bec Merge pull request #187 from dadosfera/hotfix/semantic-release
hotfix/semantic-release
2022-12-09 11:32:54 -03:00
Anderson Feitosa 519070bd82 FIX: change docs 2022-12-09 11:32:23 -03:00
Anderson Feitosa 99fc7e8a96 Merge pull request #186 from dadosfera/feature/verify-refresh-token
feature/verify-refresh-token
2022-12-09 11:25:44 -03:00
Anderson Feitosa 9fedd4bb80 Merge branch 'main' into feature/verify-refresh-token 2022-12-09 11:19:45 -03:00
Victor Radael ff5e735121 FEAT: Find a file pipeline 2022-12-09 08:29:27 -03:00
Gabriel Amorim c450d16445 Merge pull request #184 from dadosfera/feat/embed-data-asset
Feat/embed data asset
2022-12-07 16:26:49 -03:00
Gabriel Rosa 3acda53e8a npm audit fix 2022-12-07 16:17:21 -03:00
Gabriel Rosa a0f21b763a new protospack version 2022-12-07 16:14:57 -03:00
Gabriel Amorim c83d8ab1b2 Merge branch 'main' into feat/embed-data-asset 2022-12-07 15:48:04 -03:00
Gabriel Amorim 610d08fa11 Merge pull request #185 from dadosfera/fix/data-asset-docs
FIX: removed split on id on catalog POST docs route
2022-12-07 14:55:49 -03:00
Gabriel Rosa 21f68b466d FIX: removed split on id on catalog POST docs route 2022-12-07 14:49:58 -03:00
Anderson Feitosa 18b5dc980c FEAT: add verify access token path 2022-12-07 12:58:58 -03:00
Gabriel Rosa 6fdd73c58c removing treatment of undefined embed field 2022-12-07 08:36:21 -03:00
Victor Radael eaddca7eb3 FEAT: Creat a Upload CSV Pipeline 2022-12-06 18:06:32 -03:00
Gabriel Rosa 1c0377a4fa FIX: treat empty embed on update 2022-12-06 11:04:18 -03:00
Gabriel Rosa 64816e5578 FIX: treat empty embed 2022-12-06 10:07:30 -03:00
Gabriel Rosa f9064417ce docsfera.json 2022-12-06 09:11:32 -03:00
Gabriel Rosa 0b8bf9a535 Merge branch 'main' into feat/embed-data-asset 2022-12-06 09:09:43 -03:00
Victor Radael 98e865c48a FEAT: Create Upload CSV Pipeline 2022-12-05 20:35:35 -03:00
Victor Radael 2c207f068c FEAT: Create Upload CSV Pipeline 2022-12-05 20:34:04 -03:00
Gabriel Amorim 6f1e5d08c9 Merge pull request #182 from dadosfera/feat/view-table
Feat/view table
2022-12-05 18:15:05 -03:00
Gabriel Rosa b611c1eb9d Merge branch 'main' into feat/view-table 2022-12-05 18:13:36 -03:00
Gabriel Amorim d7062efc60 Merge pull request #183 from dadosfera/feat/rest-api
Feat/rest api
2022-12-05 15:22:05 -03:00
Gabriel Rosa 4a3dd98977 Merge branch 'main' into feat/rest-api 2022-12-05 13:58:22 -03:00
Gabriel Rosa 81d93ff103 FEAT: embed data asset 2022-12-05 13:22:23 -03:00
Victor Radael ea7ec52e1f FEAT: upload 50MB file 2022-12-01 18:55:48 -03:00
Victor Radael be228b7425 FEAT: upload 50MB file 2022-12-01 17:50:44 -03:00
Victor Radael 90edc6552a FEAT: Upload CSV 2022-12-01 14:57:44 -03:00
Victor Radael ed96609011 FEAT: Upload CSV 2022-12-01 14:56:41 -03:00
Gabriel Rosa d55563f62f FIX: removed split on data_asset id on DELETE route 2022-11-30 16:21:36 -03:00
Gabriel Rosa ba9575c53e FIX: return only owner username 2022-11-30 15:43:30 -03:00
Gabriel Rosa d88b154d43 FIX: new id strategy for data assets 2022-11-30 15:29:31 -03:00
Gabriel Amorim 06f4260b61 Merge pull request #181 from dadosfera/stg2
Stg2
2022-11-29 19:16:48 -03:00
Gabriel Rosa 64717c8d92 Merge branch 'main' into stg2 2022-11-29 14:41:11 -03:00
Anderson Feitosa e6e66c77e1 Merge pull request #180 from dadosfera/feature/update-pipeline-cron
feature/update-pipeline-cron
2022-11-29 14:04:31 -03:00
Gabriel Rosa 131e82b4c5 FIX: connection details improvements 2022-11-29 09:47:19 -03:00
Gabriel Rosa 32d37ecadf Removed dev deploy environment 2022-11-29 08:42:26 -03:00
Gabriel Rosa 4d667acb86 removed unused import 2022-11-25 18:21:58 -03:00
Gabriel Rosa 9d417ff61b FIX: treating new connectors upload reponse 2022-11-25 18:21:43 -03:00
Anderson Feitosa b01c4c6c25 UPGRADE: change protospack version 2022-11-25 13:00:09 -03:00
Anderson Feitosa 449a1abb63 FEAT: update auth guard to patch method 2022-11-25 12:11:51 -03:00
Anderson Feitosa 04ca17a69d FEAT: update protospack 2022-11-25 11:37:34 -03:00
Anderson Feitosa 34a0eb40a7 DOCS: docsfera patch 2022-11-23 14:58:17 -03:00
Anderson Feitosa 2df69fba84 FEAT: add log on update 2022-11-23 13:35:02 -03:00
Anderson Feitosa dcbcc5ff36 FEAT: add patch method on pipeline controller 2022-11-23 13:02:32 -03:00
Gabriel Rosa 703b5f36b3 Merge branch 'main' into feat/rest-api 2022-11-21 13:36:13 -03:00
Gabriel Rosa 54d10cb873 FIX: try to delete pipeline even if it does not exists in opensearch 2022-11-17 09:25:05 -03:00
Gabriel Amorim 569c3cf573 Merge pull request #179 from dadosfera/feat/incremental-table
Feat/incremental table
2022-11-16 16:52:35 -03:00
Gabriel Rosa c420ef62ed new protospack version 2022-11-16 16:46:38 -03:00
Alexsander Pereira 966ba00fec Merge pull request #178 from dadosfera/stg2
FEAT: Adding stg2
2022-11-16 15:47:49 -03:00
Alexsander Pereira 0aadf7fc40 FEAT: Adding stg2 2022-11-16 15:18:18 -03:00
Gabriel Rosa 3e3268cb26 FIX: New getAvailableEntities payload 2022-11-16 10:37:21 -03:00
Anderson Feitosa 09bd623b62 Merge pull request #177 from dadosfera/catalog-permissions
FIX: create data asset permission public
2022-11-14 16:19:09 -03:00
Gabriel Rosa 4bf9a5831a FIX: create data asset permission public 2022-11-14 16:15:57 -03:00
Anderson Feitosa 264077289b Merge pull request #176 from dadosfera/catalog-permissions
FIX: renamed catalog create permission
2022-11-14 15:52:04 -03:00
Gabriel Rosa 14c686cff4 FIX: renamed catalog create permission
- Catalog controller using permission decorator on every route
2022-11-14 15:40:38 -03:00
Gabriel Amorim 0e57667419 Merge pull request #175 from dadosfera/feat/modules-permissions
Feat/modules permissions
2022-11-11 12:38:04 -03:00
Gabriel Rosa ac7dc4905e FIX: changed PROCESS permisison group title 2022-11-11 12:35:04 -03:00
Gabriel Rosa 94282718ad Changed permission group name 2022-11-11 12:27:57 -03:00
Gabriel Rosa 7c228c5c53 FIX: set new module permissions public 2022-11-11 12:26:14 -03:00
Gabriel Amorim 14e0f3d2d3 Merge pull request #174 from dadosfera/fix/event-tracker
Fix/event tracker
2022-11-11 10:18:42 -03:00
Victor Radael 85ef0d7c74 Merge remote-tracking branch 'origin' into fix/event-tracker 2022-11-11 10:16:07 -03:00
Victor Radael e00b6481d0 FIX: Tracker 2022-11-11 10:15:49 -03:00
Victor Radael 8906c73d84 Revert "FIX: Event Tracker"
This reverts commit 3bc37db891.
2022-11-11 10:13:24 -03:00
Victor Radael 3bc37db891 FIX: Event Tracker 2022-11-11 10:12:20 -03:00
Gabriel Rosa 3c877b8b8d Merge branch 'main' into feat/rest-api 2022-11-10 15:14:45 -03:00
Gabriel Amorim fe44664970 Merge pull request #173 from dadosfera/fix/modules-permissions
FIX: new modules permissions
2022-11-10 15:13:01 -03:00
Gabriel Rosa 0503732c1e new protospack version 2022-11-10 14:43:46 -03:00
Gabriel Rosa fa0855d631 FIX: new modules permissions 2022-11-10 12:57:15 -03:00
Gabriel Amorim 38aee30f2a Merge pull request #172 from dadosfera/fix/pipeline-details-bug
Fix/pipeline details bug
2022-11-10 10:32:42 -03:00
Gabriel Rosa 19f5c947bd Set release version on swagger when deploying main 2022-11-10 10:12:14 -03:00
Gabriel Rosa 44aa0e6010 improved run prod command 2022-11-10 09:47:30 -03:00
Gabriel Rosa cf4e7da1d8 Typing login route 2022-11-10 09:46:22 -03:00
Gabriel Rosa 8bd7d54aa7 new protospack version 2022-11-10 09:16:02 -03:00
Gabriel Rosa a00a66e0d0 Merge branch 'main' into fix/pipeline-details-bug 2022-11-10 08:41:36 -03:00
Victor Radael f4c5d03269 Merge pull request #171 from dadosfera/ft/mixpanel-track
FEAT: Mixpanel Tracker
2022-11-10 08:35:55 -03:00
Victor Radael 060ba7da8a FEAT: Mixpanel Tracker 2022-11-09 20:46:40 -03:00
Victor Radael 5ec2c01b20 FEAT: Mixpanel Tracker 2022-11-09 20:37:39 -03:00
Gabriel Rosa 4a7e660a49 FIX: fixed condition of getObjValueFromPath 2022-11-09 20:06:23 -03:00
Gabriel Rosa abc6258492 FIX: fixed condition to set default value on pipeline details 2022-11-09 19:30:13 -03:00
Gabriel Rosa 7ee1215da2 Deploy docs using github hosted runner 2022-11-09 18:48:50 -03:00
Gabriel Rosa cd5886337b FIX: customer modules 2022-11-09 18:40:53 -03:00
Gabriel Rosa e6b0034805 Merge branch 'main' into feat/rest-api 2022-11-09 18:29:04 -03:00
Gabriel Rosa b9c1f784d3 FIX: treatingconnector upload response 2022-11-09 18:25:57 -03:00
Gabriel Rosa 5fb8dae312 FIX: pipeline details now showing correct default values on s3 pipelines 2022-11-09 17:35:18 -03:00
Gabriel Amorim 5072a54ef6 Merge pull request #170 from dadosfera/fix/revert-action-version
FIX: return semantic release action to previous version
2022-11-09 16:05:04 -03:00
Gabriel Rosa 718f5cb12a FIX: return sematic release action to previous version 2022-11-09 15:56:28 -03:00
Gabriel Amorim 4c41dd8029 Merge pull request #169 from dadosfera/fix/revert-action-version
FIX: remove `conventional-changelog-eslint` plugin
2022-11-09 15:41:08 -03:00
Gabriel Rosa fe8a0b88b0 FIX: remove conventional-changelog-eslint plugin 2022-11-09 15:39:37 -03:00
Gabriel Rosa 3edf5c2416 Merge branch 'main' into feat/rest-api 2022-11-09 15:03:09 -03:00
Gabriel Amorim cd67ca61cd Merge pull request #168 from dadosfera/fix/customer-modules
FIX: customer modules
2022-11-09 14:59:54 -03:00
Gabriel Rosa b9439685f3 FIX: new swagger info 2022-11-09 14:29:32 -03:00
Gabriel Rosa 04edce0c56 FIX: new protospack version 2022-11-09 14:08:05 -03:00
Gabriel Rosa f3e00a15c8 Remove unused imports 2022-11-09 14:06:30 -03:00
Gabriel Rosa 0422c7a369 Set branch name on swagger version 2022-11-09 14:06:12 -03:00
Gabriel Rosa f22d9370db test new upload documentation 2022-11-09 11:27:54 -03:00
Victor Radael ef098dd74d Merge pull request #167 from dadosfera/ft/create-data-asset
FEAT: Ft/create data asset
2022-11-08 19:48:48 -03:00
Victor Radael 2541960b44 FEAT: Create Data Asset 2022-11-08 19:32:26 -03:00
Victor Radael 2dc965ed90 FIX:delete and update data asset 2022-11-08 14:18:31 -03:00
Gabriel Rosa a9ee74724f FIX: better error handling 2022-11-08 12:11:05 -03:00
Victor Radael b2f1ce669d FIX: Save 2022-11-08 11:41:34 -03:00
Victor Radael 602d81e51f FIX: Save 2022-11-08 11:39:48 -03:00
Rodrigo Zamboni bbdb023dde Merge pull request #165 from dadosfera/feat/newCustomersInfo
Feat/new customers info
2022-11-07 16:05:33 -03:00
Gabriel Rosa 9cb4cd7093 FIX: fixed protospack version 2022-11-07 13:48:28 -03:00
Gabriel Rosa efc5870d87 FIX: fixed protospack version 2022-11-07 13:48:09 -03:00
Rodrigo Zamboni c3d33f8230 Merge branch 'main' into feat/newCustomersInfo 2022-11-07 12:38:13 -03:00
rodrigo.zamboni 0654e8fa04 FIX: updaetd protospack version 2022-11-07 12:35:51 -03:00
Gabriel Amorim 28ab892208 Merge pull request #164 from dadosfera/catalog-permissions
FIX: Catalog permissions
2022-11-07 10:44:02 -03:00
Gabriel Rosa d1522eb0ea FIX: new protospack version 2022-11-04 17:57:48 -03:00
Gabriel Rosa 6be9e06b22 FIX: new protospack version 2022-11-04 14:22:07 -03:00
Gabriel Rosa 7af261d56c New permissions for transformations 2022-11-04 12:03:15 -03:00
Gabriel Rosa 8fa924fdfb Changed delete description 2022-11-04 09:27:53 -03:00
Victor Radael 4b4bdfea61 FEAT: Create Data Asset 2022-11-03 17:18:20 -03:00
Gabriel Rosa d1064c852b rename workflow job 2022-11-03 12:03:25 -03:00
Gabriel Rosa 63338f5250 fixed deploy worflow file 2022-11-03 11:59:01 -03:00
Gabriel Rosa 5e1fb41930 FIX: identation 2022-11-03 11:57:52 -03:00
Gabriel Rosa a807da2414 test deploy 2022-11-03 11:48:46 -03:00
Gabriel Rosa 85f14fcf4b FIX: generate better summary on deploy 2022-11-03 11:28:09 -03:00
Gabriel Rosa d95898ce55 FIX: generate better summary on deploy 2022-11-03 11:18:02 -03:00
Gabriel Rosa 33075b0f85 FIX: generate better summary on deploy 2022-11-03 11:10:38 -03:00
Gabriel Rosa ca75f8f6e7 FIX: generate better summary on deploy 2022-11-03 11:00:55 -03:00
Gabriel Rosa af12a39959 FEAT: Deploy summary 2022-11-03 10:53:37 -03:00
Gabriel Rosa f77476b9ae FIX: echo text 2022-11-03 10:33:55 -03:00
Gabriel Rosa fb0c659cd1 FIX: permissions for catalog 2022-11-03 10:27:50 -03:00
Gabriel Rosa aea6815de7 Merge branch 'main' into catalog-permissions 2022-11-03 10:20:10 -03:00
Gabriel Amorim 0103f100f5 Merge pull request #163 from dadosfera/new-ecr-image
FIX: new ecr image
2022-11-01 15:59:49 -03:00
Gabriel Rosa 66b6be43b7 FIX: new ecr image 2022-11-01 15:33:14 -03:00
Gabriel Rosa c029e286b5 WIP 2022-11-01 15:31:45 -03:00
Gabriel Amorim a8f94a2d53 Merge pull request #162 from dadosfera/feat/platform-delete-pipeline
Feat: platform delete pipeline
2022-10-31 16:43:44 -03:00
Gabriel Rosa aadb3fcdee Better inputs error codes 2022-10-31 16:20:11 -03:00
Gabriel Rosa a26e88d2b6 Merge branch 'main' into feat/platform-delete-pipeline 2022-10-31 15:37:13 -03:00
Victor Radael b20f67e8b8 Merge pull request #161 from dadosfera/ft/delete-data-assets
Ft/delete data assets
2022-10-27 18:44:08 -03:00
Victor Radael 4a8a8eb463 FIX: Pipeline delete 2022-10-27 18:20:56 -03:00
Victor Radael d52f8b78c8 FIX: Pipeline delete 2022-10-27 18:06:39 -03:00
Victor Radael bfbe7cce87 Merge pull request #160 from dadosfera/ft/delete-data-assets
Ft/delete data assets
2022-10-27 17:14:22 -03:00
Victor Radael 1ab8c1dd10 FIX: Merge origin main 2022-10-27 15:24:04 -03:00
Gabriel Amorim 246de83142 Merge pull request #159 from dadosfera/ref/catalog
Catalog Refactor
2022-10-26 17:40:42 -03:00
Victor Radael dfa6222538 FIX: Delete data asset 2022-10-26 14:22:29 -03:00
Gabriel Rosa fc7a9c7909 FIX: do not throw errors when deleting input or transformation on pipeline delete 2022-10-26 10:52:12 -03:00
Gabriel Rosa 932616a578 Delete connection permission public 2022-10-26 09:09:28 -03:00
Victor Radael b64619ede1 FIX: Remove trash code 2022-10-25 19:29:23 -03:00
Gabriel Rosa 4a08e5f560 Merge branch 'main' into ref/catalog 2022-10-25 17:33:04 -03:00
Gabriel Rosa 4ffd0916c3 FIX: pipeline delete working 2022-10-25 16:21:58 -03:00
Victor Radael 92041f453c FIX: Remove trash code 2022-10-25 14:00:26 -03:00
Gabriel Rosa f2f09cc9c7 FIX: oauth redirect adjusted 2022-10-24 15:24:14 -03:00
Victor Radael d486231fd6 FEAT: RLS new barnch 2022-10-21 15:44:21 -03:00
Victor Radael 526a3bddcd Merge pull request #158 from dadosfera/ft/rls-old-catalog
FEAT: Rls first change
2022-10-21 15:18:01 -03:00
Victor Radael 45108d3019 FEAT: Rls first change 2022-10-21 14:49:28 -03:00
Victor Radael ac3e26b851 FIX: RLS 2022-10-21 14:36:59 -03:00
Gabriel Rosa 7fbc919716 FIX: catalog filters 2022-10-20 11:23:30 -03:00
Gabriel Rosa 5b1eb19511 FIX: catalog sort by 2022-10-20 10:27:38 -03:00
Victor Radael b714f3cf36 FIX: Filters 2022-10-20 10:04:45 -03:00
Victor Radael 48c1e29168 FIX: Filters 2022-10-20 09:45:12 -03:00
Victor Radael 04ea1d0f04 FIX: Get a total and fix owner type 2022-10-19 17:01:22 -03:00
Victor Radael 0de5e1d5d1 PATCH: Ref/Catalog new functions 2022-10-18 14:13:24 -03:00
Victor Radael b8c41fad1b PATCH: Ref/Catalog new functions 2022-10-18 13:43:18 -03:00
Victor Radael ef9ec5ad42 PATCH: Ref/Catalog new functions 2022-10-18 13:05:47 -03:00
Victor Radael 65e71f21a0 Merge branch 'ref/catalog' of github.com:dadosfera/maestro into ref/catalog 2022-10-18 13:05:27 -03:00
Victor Radael b921393ba1 PATCH: Ref/Catalog new functions 2022-10-18 13:05:05 -03:00
Gabriel Rosa 18aa7d8674 FIX: permission language fixed 2022-10-17 17:53:47 -03:00
Victor Radael f2dd871de2 FIX: Ref Catalogo new functions models 2022-10-17 16:16:31 -03:00
Victor Radael cf3fa88196 FIX: Ref Catalogo new functions models 2022-10-17 15:00:29 -03:00
Victor Radael 131810533c FIX: Ref Catalogo new functions models 2022-10-17 14:58:11 -03:00
Victor Radael ea06f3f571 FIX: Ref Catalogo new functions models 2022-10-17 14:51:28 -03:00
Victor Radael 81921a7eb2 FIX: Ref Catalogo new functions models 2022-10-17 14:38:31 -03:00
Victor Radael e9fdd0285b FIX: Ref Catalogo new functions models 2022-10-17 13:42:13 -03:00
Victor Radael 167470adba FIX: Ref Catalogo new functions models 2022-10-17 13:35:13 -03:00
Victor Radael fbb9a59214 FIX: Ref Catalogo new functions models 2022-10-17 13:32:41 -03:00
Gabriel Amorim ef6d8594a3 Merge pull request #157 from dadosfera/fix/get-user-permissions
FIX: new function to get all user permissions
2022-10-14 11:57:05 -03:00
Gabriel Rosa ac73c0b046 FIX: new function to get all user permissions 2022-10-14 11:33:15 -03:00
Gabriel Amorim 5a30bf08b2 Merge pull request #156 from dadosfera/fix/pipeline-properties
Fix/pipeline properties
2022-10-13 16:39:41 -03:00
Gabriel Rosa 62b4eb709e Return properties object on findOneProperties 2022-10-13 16:24:32 -03:00
Gabriel Rosa b37a18d1da Merge branch 'main' into fix/pipeline-properties 2022-10-13 15:59:53 -03:00
Gabriel Rosa 379bac704f FIX: getPipelineproperties 2022-10-13 14:59:27 -03:00
Gabriel Rosa 9178530a21 FEAT: get pipeline properties 2022-10-13 14:58:41 -03:00
Gabriel Amorim 667d3f874d Merge pull request #155 from dadosfera/fix/user-permissions
FIX: permissions array on user findOneById
2022-10-13 14:03:02 -03:00
Victor Radael 475af224bf Merge remote-tracking branch 'origin' into ref/catalog 2022-10-13 11:43:16 -03:00
Gabriel Rosa 11f1df5537 FIX: permissions array on user findOneById 2022-10-13 09:23:56 -03:00
Victor Radael e3b02571c8 Merge pull request #154 from dadosfera/fix/oauth
FIX: Public oauth route
2022-10-13 09:05:29 -03:00
Gabriel Rosa 979a12aa7f Merge branch 'main' into fix/oauth 2022-10-13 09:02:17 -03:00
Gabriel Rosa 7646d7a3c5 WIP 2022-10-13 08:57:21 -03:00
Victor Radael 8f0e851a88 FIX: Public oauth route 2022-10-12 23:12:33 -03:00
Victor Radael 779418f471 clear catalog 2022-10-11 22:23:36 -03:00
Victor Radael 85f41521ed Merge remote-tracking branch 'origin' into ref/catalog 2022-10-11 15:36:08 -03:00
Victor Radael e2bb119745 refactor/catalog 2022-10-11 15:35:29 -03:00
144 changed files with 25705 additions and 13567 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'prettier',
],
root: true,
env: {
+111 -100
View File
@@ -3,16 +3,21 @@ on:
push:
branches:
- main
- beta
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
description: "Deployment environment"
required: true
type: choice
options:
- dev
- stg
- prd
push_to_dockerhub:
description: "Push image to Dockerhub?"
required: true
type: boolean
default: false
jobs:
extract_environment:
@@ -26,9 +31,11 @@ jobs:
- name: Extract Environment
run: |
if [ ${EVENT} == "workflow_dispatch" ]; then
echo "##[set-output name=environment;]$(echo ${DEPLOY_ENV})"
echo "environment=${DEPLOY_ENV}" >> $GITHUB_OUTPUT
elif [ ${GITHUB_REF} == "refs/heads/main" ]; then
echo "##[set-output name=environment;]$(echo "prd")"
echo "environment=prd" >> $GITHUB_OUTPUT
elif [ ${GITHUB_REF} == "refs/heads/beta" ]; then
echo "environment=stg" >> $GITHUB_OUTPUT
fi
id: extract_environment
@@ -39,28 +46,35 @@ jobs:
new_release_version: ${{ (steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version) || (github.event_name == 'workflow_dispatch' && '0.0.0') }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- if: github.event_name != 'workflow_dispatch'
name: Semantic Release
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v3
id: semantic
with:
semantic_version: 16
extra_plugins: |
conventional-changelog-eslint
conventional-changelog-eslint@4.0.0
branches: |
[
'main'
'main',
{
name: 'alpha',
prerelease: true
},
{
name: 'beta',
prerelease: true
}
]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
deploy:
build_ecr_image:
if: ${{ github.event_name == 'workflow_dispatch' || needs.semantic_release.outputs.new_release_published == 'true' }}
needs: [extract_environment, semantic_release]
runs-on:
[self-hosted, '${{ needs.extract_environment.outputs.environment }}']
[self-hosted, "prd"]
steps:
- name: Printing stats
@@ -71,16 +85,12 @@ jobs:
run: echo ${GITHUB_REF#refs/heads/}
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Update Pip
run: |
python3 -m pip install --upgrade pip
- name: Install Docker Compose
run: |
python3 -m pip install docker-compose --upgrade
- name: Install AWS CLI
run: |
python3 -m pip install awscli --upgrade
@@ -90,14 +100,14 @@ jobs:
python3 -m pip install awsebcli --upgrade
- name: Configure AWS Region
uses: aws-actions/configure-aws-credentials@v1
uses: aws-actions/configure-aws-credentials@v4
id: aws
with:
aws-region: us-east-1
- name: Login to AWS ECR
id: login_ecr
uses: aws-actions/amazon-ecr-login@v1
uses: aws-actions/amazon-ecr-login@v2
- name: Build, Tag, and Push Image to AWS ECR
env:
@@ -105,102 +115,103 @@ jobs:
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
run: |
docker-compose -f build.docker-compose.yml build
docker-compose -f build.docker-compose.yml push
docker compose -f build.docker-compose.yml build
docker compose -f build.docker-compose.yml push
- name: Create ZIP file to Deploy AWS Beanstalk
- name: Login to Docker Hub
if: ${{inputs.push_to_dockerhub}}
uses: docker/login-action@v2
with:
username: dadosfera
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build, Tag, and Push Image to Dockerhub
if: ${{inputs.push_to_dockerhub}}
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
run: |
sed -i -e "s/\${ENV}/$ENV/g" docker-compose.yml
sed -i -e "s/\${IMAGE_TAG}/$IMAGE_TAG/g" docker-compose.yml
sed -i -e "s/\${ACCOUNT_ID}/$ACCOUNT_ID/g" docker-compose.yml
zip deploy.zip docker-compose.yml -r .ebextensions
docker compose -f build.docker-compose.dockerhub.yml build
docker compose -f build.docker-compose.dockerhub.yml push
- name: Deploy AWS Beanstalk
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
AWS_REGION: us-east-1
APP_NAME: ${{ github.event.repository.name }}
run: |
eb use $APP_NAME-$ENV
echo -e "deploy:\n artifact: deploy.zip" >> .elasticbeanstalk/config.yml
eb deploy
# - name: Create ZIP file to Deploy AWS Beanstalk
# env:
# ENV: ${{ needs.extract_environment.outputs.environment }}
# IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
# ACCOUNT_ID: ${{ steps.aws.outputs.aws-account-id }}
# NODE_EXPORTER_URL: ${{needs.extract_environment.outputs.environment == 'prd' && '611330257153.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest' || '468720548566.dkr.ecr.us-east-1.amazonaws.com\/monitoring\/node_exporter:latest'}}
# run: |
# sed -i -e "s/\${ENV}/$ENV/g" docker-compose.yml
# sed -i -e "s/\${IMAGE_TAG}/$IMAGE_TAG/g" docker-compose.yml
# sed -i -e "s/\${ACCOUNT_ID}/$ACCOUNT_ID/g" docker-compose.yml
# sed -i -e "s/\${NODE_EXPORTER_URL}/$NODE_EXPORTER_URL/g" docker-compose.yml
# zip deploy.zip docker-compose.yml -r .ebextensions
# - name: Deploy AWS Beanstalk
# env:
# ENV: ${{ needs.extract_environment.outputs.environment }}
# AWS_REGION: us-east-1
# APP_NAME: ${{ github.event.repository.name }}
# run: |
# eb use $APP_NAME-$ENV
# echo -e "deploy:\n artifact: deploy.zip" >> .elasticbeanstalk/config.yml
# eb deploy
- name: Remove Docker's Trash
if: always()
run: |
docker system prune --volumes -a -f
docker system df
api_docs:
needs: [extract_environment, semantic_release, deploy]
runs-on:
[self-hosted, '${{ needs.extract_environment.outputs.environment }}']
helmfile-deploy:
needs: [extract_environment, semantic_release, build_ecr_image]
runs-on: [self-hosted, "prd-azure"]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout code
uses: actions/checkout@v3
- name: Extract Docs PageId
- name: Set up Helm
uses: azure/setup-helm@v1
with:
version: 'v3.9.0'
- name: Install Azure ClI
run: |
curl -sL https://aka.ms/InstallAzureCLIDeb | bash
- uses: azure/login@v2
with:
creds: '{"clientId":"${{ secrets.ARM_CLIENT_ID }}","clientSecret":"${{ secrets.ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.ARM_TENANT_ID }}"}'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: Install Helmfile
run: |
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
mv helmfile /usr/local/bin/
helmfile --version
- name: Install Helm Diff Plugin
run: helm plugin install https://github.com/databus23/helm-diff || true
- name: Setup kubectl
uses: azure/setup-kubectl@v1
with:
version: 'v1.30.1'
- name: Authenticate with cluster
env:
CLUSTER_NAME: platform-${{ needs.extract_environment.outputs.environment }}
run: az aks get-credentials --resource-group dadosfera-prd --name ${CLUSTER_NAME} --overwrite-existing
- name: Run Helmfile Apply
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
DEV_DOCS_PAGE_ID: ${{ secrets.DEV_DOCS_PAGE_ID }}
STG_DOCS_PAGE_ID: ${{ secrets.STG_DOCS_PAGE_ID }}
PRD_DOCS_PAGE_ID: ${{ secrets.PRD_DOCS_PAGE_ID }}
shell: bash
run: |
if [ $ENV == "dev" ]; then
echo "##[set-output name=result;]$(echo $DEV_DOCS_PAGE_ID)"
elif [ $ENV == "stg" ]; then
echo "##[set-output name=result;]$(echo $STG_DOCS_PAGE_ID)"
elif [ $ENV == "prd" ]; then
echo "##[set-output name=result;]$(echo $PRD_DOCS_PAGE_ID)"
fi
id: extract_docs_page_id
- name: Extract Docs BlockId
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
DEV_DOCS_BLOCK_ID: ${{ secrets.DEV_DOCS_BLOCK_ID }}
STG_DOCS_BLOCK_ID: ${{ secrets.STG_DOCS_BLOCK_ID }}
PRD_DOCS_BLOCK_ID: ${{ secrets.PRD_DOCS_BLOCK_ID }}
shell: bash
run: |
if [ $ENV == "dev" ]; then
echo "##[set-output name=result;]$(echo $DEV_DOCS_BLOCK_ID)"
elif [ $ENV == "stg" ]; then
echo "##[set-output name=result;]$(echo $STG_DOCS_BLOCK_ID)"
elif [ $ENV == "prd" ]; then
echo "##[set-output name=result;]$(echo $PRD_DOCS_BLOCK_ID)"
fi
id: extract_docs_block_id
- name: Generate API docs
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
DOCS_URL: ${{ secrets.DOCS_URL }}
DOCS_API_TOKEN: ${{ secrets.DOCS_API_TOKEN }}
DOCS_PAGE_ID: ${{ steps.extract_docs_page_id.outputs.result }}
DOCS_BLOCK_ID: ${{ steps.extract_docs_block_id.outputs.result }}
continue-on-error: true
run: |
SWAGGER=`cat swagger.txt`
curl --request POST \
--url $DOCS_URL \
--header 'Content-Type: application/json' \
--data @<(cat <<EOF
{
"service": "$DOCS_API_TOKEN",
"pageId": "$DOCS_PAGE_ID",
"blockId": "$DOCS_BLOCK_ID",
"schema": $SWAGGER
}
EOF
)
- name: Cleanup Docker's Leftovers
if: always()
continue-on-error: true
run: |
docker system prune
docker rmi -f $(docker images -aq)
IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }}
run: helmfile -f helmfiles/${ENV}.yaml sync --set image.tag=$IMAGE_TAG
+6 -11
View File
@@ -6,23 +6,18 @@ on:
jobs:
test:
runs-on: self-hosted
runs-on: [self-hosted, prd]
env:
APP_NAME: ${{ github.event.repository.name }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Build
env:
APP_NAME: ${{ github.event.repository.name }}
run: |
docker build -t $APP_NAME --target test .
run: docker build -t ${APP_NAME}_teste --target test .
- name: Run Test
env:
ENV: test
APP_NAME: ${{ github.event.repository.name }}
run: |
docker run --rm --entrypoint="npm" $APP_NAME run test
run: docker run ${APP_NAME}_teste
- name: Remove Docker's Trash
if: always()
+79
View File
@@ -0,0 +1,79 @@
name: Validate K8S Modifications
on:
pull_request:
branches:
- main
- stg
jobs:
extract_environment:
runs-on: ubuntu-22.04
outputs:
environment: ${{ steps.extract_environment.outputs.environment }}
steps:
- name: Extract Environment
run: |
if [ "${{ github.event.pull_request.base.ref }}" == "main" ]; then
echo "environment=prd" >> $GITHUB_OUTPUT
elif [ "${{ github.event.pull_request.base.ref }}" == "beta" ]; then
echo "environment=stg" >> $GITHUB_OUTPUT
fi
id: extract_environment
helmfile-deploy:
needs: [extract_environment]
runs-on: [self-hosted, "prd-azure"]
steps:
- name: Summary
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
run: |
echo "### :rocket: Deploy da branch \`$GITHUB_REF_NAME\` para o environment ($ENV)" >> $GITHUB_STEP_SUMMARY
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Helm
uses: azure/setup-helm@v1
with:
version: 'v3.9.0'
- name: Install Azure ClI
run: |
curl -sL https://aka.ms/InstallAzureCLIDeb | bash
- uses: azure/login@v2
with:
creds: '{"clientId":"${{ secrets.ARM_CLIENT_ID }}","clientSecret":"${{ secrets.ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.ARM_TENANT_ID }}"}'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: Install Helmfile
run: |
wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz
tar -xzf helmfile_0.148.0_linux_amd64.tar.gz
mv helmfile /usr/local/bin/
helmfile --version
- name: Install Helm Diff Plugin
run: helm plugin install https://github.com/databus23/helm-diff || true
- name: Setup kubectl
uses: azure/setup-kubectl@v1
with:
version: 'v1.30.1'
- name: Authenticate with cluster
env:
CLUSTER_NAME: platform-${{ needs.extract_environment.outputs.environment }}
run: az aks get-credentials --resource-group dadosfera-prd --name ${CLUSTER_NAME} --overwrite-existing
- name: Run Helmfile Diff
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
run: helmfile -f helmfiles/${ENV}.yaml diff
+1 -1
View File
@@ -1 +1 @@
18.3.0
18.17
+1
View File
@@ -12,6 +12,7 @@
"start:debug"
],
"runtimeExecutable": "npm",
"runtimeVersion": "18.17",
"skipFiles": [
"<node_internals>/**"
],
+61 -16
View File
@@ -1,21 +1,66 @@
# install all dependencies
FROM node:18.3.0-alpine3.15 as packages
FROM node:18.17-alpine AS base_image
FROM base_image AS build_base
WORKDIR /app
COPY package.json package-lock.json ./
RUN apk add --no-cache aws-cli \
&& aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1 \
&& npm install
RUN apk update
# needed packages to build dependencies from source
RUN apk add --no-cache \
aws-cli \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont
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
FROM build_base AS ci_image
RUN aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1
RUN npm ci
COPY . .
# unit test specific build
FROM node:18.3.0-alpine3.15 as test
WORKDIR /app
COPY --from=packages /app/node_modules ./node_modules
COPY . ./
FROM ci_image AS test
ENV DUC_URL=0.0.0.0:50051
ENTRYPOINT ["npm", "run", "test"]
FROM node:18.3.0-alpine3.15
WORKDIR /app
COPY . /app/
COPY --from=packages /app/node_modules ./node_modules
# dev build
FROM build_base AS dev
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1
# flag --build-from-source is required to force-build sqlite3
RUN npm ci
COPY . .
ENTRYPOINT npm run start:dev
FROM ci_image AS prod_build
RUN npm run build
EXPOSE 3333
ENTRYPOINT npm run start
FROM base_image
WORKDIR /app
COPY --from=prod_build /app/dist ./dist
COPY --from=prod_build /app/node_modules ./node_modules
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
+10
View File
@@ -0,0 +1,10 @@
LICENSE
Copyright (C) 2023 Dadosfera
All rights reserved.
This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means.
Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.
If you have any questions about this, please contact Dadosfera.ai at legal@dadosfera.ai
+175 -122
View File
@@ -1,68 +1,103 @@
<p align="center">
<image src="./assets/maestro.svg" style="width:10rem">
</p>
</p>
# Maestro
Maestro is the Dadosfera's gateway, it's responsible for the communication between the frontend application and Dadosfera's microservices.
## 🚀 Starting
These instructions will allow you to get a working copy of the project on your local machine for development and testing purposes.
Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços.
### 📋 Requirements
- [NodeJS v18.3.0 LTS / NPM v8.11](https://nodejs.org/pt-br/download/) (you can opt to use [NVM](https://github.com/nvm-sh/nvm) to easily manage node versions)
- Request access to AWS Console dev account for **all services** (avoid gradually asking for each needed service. it will slow down your development cycle)
- Create your Access Key on the "Security credentials" menu
- Set the Access Key on your local development machine
- Request access to the dev, stg and prd VPNs
```mermaid
graph TD;
Frontend<-->Maestro;
Maestro<-->duc;
Maestro<-->pi-factory;
Maestro<-->in-factory;
```
### 🔧 Installation<a id="installation"></a>
- Clone the repository
- SSH
```
git clone git@github.com:dadosfera/maestro.git
```
or
É uma API REST, desenvolvida em NodeJs utilizando o Framework [NestJs](https://docs.nestjs.com/).
- HTTPS
```
git clone https://github.com/dadosfera/maestro.git
```
## 🚀 Iniciando
- Select the correct node version (optional, only if using [NVM](https://github.com/nvm-sh/nvm)):
```sh
nvm use
```
Estas instruções permitirão que você obtenha uma cópia funcional do projeto em sua máquina local para desenvolvimento e testes.
- Install the project dependencies:
```sh
npm i
```
### 📋 Requisitos
- Setup the following enviroment variables:
```
ENV=
DUC_URL=
INFACTORY_URL=
OTFACTORY_URL=
PIFACTORY_URL=
SM_OAUTH_PATH=
```
- [NodeJS v18.17 / NPM v9.6.7](https://nodejs.org/pt-br/download/)
- Start the server:
```sh
# dev mode
npm run start:dev
> Dica: Utilize [NVM](https://github.com/nvm-sh/nvm) para gerenciar facilmente as versões do node
# or in debug mode
npm run start:debug
```
The service should start successfully.
- Solicite acesso à conta de desenvolvimento do AWS Console para **todos os serviços necessários**
- Crie sua Access Key no menu "Security credentials"
- Defina a Access Key em sua máquina de desenvolvimento local
- Solicite acesso às VPNs de stg e prd
## Authentication decorators
Maestro have utilities to ease the user authentication on every controller and route. The following decorators are available:
### 🔧 Instalação<a id="installation"></a>
- Clone o repositório
```sh
git clone git@github.com:dadosfera/maestro.git
```
- Selecione a versão correta do node (opcional, apenas se estiver usando o [NVM](https://github.com/nvm-sh/nvm)):
```sh
nvm use
```
- Instale as dependências do projeto:
```sh
npm i
```
- Configure as seguintes variáveis de ambiente:
```
ENV=
DUC_URL=
INFACTORY_URL=
OTFACTORY_URL=
PIFACTORY_URL=
SM_OAUTH_PATH=
```
- Inicie o servidor:
```sh
npm run start:dev
```
> Se preferir, utilize o debbuger do VSCode apertando F5
O serviço deve iniciar com sucesso.
## 📄 Documentação
O NestJs facilita a documentação de cada rota usando decoradores em todas as propriedades de solicitações e respostas. Ele gera automaticamente um swagger para as informações fornecidas e fornece uma rota para acessá-lo em http://localhost:3333/api. Para mais informações, consulte a [documentação oficial](https://docs.nestjs.com/openapi/introduction).
### - Documentações múltiplas
Atualmente, estamos gerando **2 documentações diferentes**: Interna e Externa.
Todas as rotas que têm o decorador `@ApiInternalOnly()` não serão visíveis no swagger da API **Externa**.
- Quando você inicia a aplicação com `npm run start:dev`, ela servirá e gerará o JSON do swagger da API **Interna**
- Quando você executa `npm run docs`, ele gerará o JSON do swagger de ambas as APIs **Interna** e **Externa** e os salvará em `docsfera.json` e `docsfera.external.json`, respectivamente;
> Link para documentação interna: https://dadosfera.github.io/docsfera
É importante executar `npm run docs` antes de cada implantação para que sempre tenhamos as documentações mais atualizadas publicadas.
## Decoradores de autenticação
O Maestro possui utilitários para facilitar a autenticação do usuário em todos os controladores e rotas. Os seguintes decoradores estão disponíveis:
### `@Authenticated`
If the user must be authenticated to make request, we can use the `@Authenticated` decorator in the controller or route, as needed.
Se o usuário precisar estar autenticado para fazer uma solicitação, podemos usar o decorador `@Authenticated` no controlador ou rota, conforme necessário.
```ts
import { Authenticated } from '../../authentication/authentication.decorator';
@@ -74,7 +109,7 @@ class FooController {
@Get('bar')
async getBar() {
this.logger.info('user is authenticated!');
this.logger.info('usuário está autenticado!');
return { authenticated: true };
}
@@ -91,14 +126,14 @@ class FooController {
@Get('bar')
@Authenticated()
async getBar() {
this.logger.info('user is authenticated!');
this.logger.info('usuário está autenticado!');
return { authenticated: true };
}
@Post('bar')
async postBar() {
this.logger.info('user is NOT authenticated!');
this.logger.info('usuário NÃO está autenticado!');
return { authenticated: false };
}
@@ -106,7 +141,8 @@ class FooController {
```
### `@RequireAllPermissions`
This decorator requires that **all permissions** listed are granted to the requesting user.
Este decorador exige que **todas as permissões** listadas sejam concedidas ao usuário solicitante.
```ts
import { RequireAllPermissions } from '../../authentication/authentication.decorator';
@@ -125,7 +161,8 @@ class FooController {
```
### `@RequireSomePermission`
In the following case, the user is required to have **at least one** listed permission.
No caso seguinte, é necessário que o usuário tenha **pelo menos uma** das permissões listadas.
```ts
import { RequireSomePermission } from '../../authentication/authentication.decorator';
@@ -144,17 +181,19 @@ class FooController {
```
### `@AuthenticateCondition`
If a more complicated authentication check needs to be done, we can use the `@AuthenticateCondition` decorator to define it. The custom function must return `true` to authenticate the request.
In the following example:
- all routes on the `FooController` controller can only be requested from localhost
- `POST /foo/bar` can only be requested from localhost **and** by users from customer id `111...eef`
Se for necessária uma verificação de autenticação mais complicada, podemos usar o decorador `@AuthenticateCondition` para defini-la. A função personalizada deve retornar `true` para autenticar a solicitação.
No exemplo a seguir:
- todas as rotas no controlador `FooController` só podem ser solicitadas a partir do localhost
- `POST /foo/bar` só pode ser solicitado a partir do localhost **e** por usuários do cliente com id `111...eef`
```ts
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
@Controller('foo')
// allow requests only from localhost
// permitir solicitações apenas do localhost
@AuthenticateCondition((request: Request) => request.ip === '::ffff:127.0.0.1')
class FooController {
/* ... */
@@ -165,61 +204,68 @@ class FooController {
}
@Post('bar')
// allow requests only from a specific customer
@AuthenticateCondition((request: Request, user: RequestUser) => user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef')
// permitir solicitações apenas de um cliente específico
@AuthenticateCondition(
(request: Request, user: RequestUser) =>
user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef',
)
async postBar() {
/* ... */
}
}
```
### Note on authentication decorators
- The old authentication method placed the user data in the `request.body.info` field, this imposes certain issues regarding the request body because this data should be from the frontend without any modification by Maestro. Now this usage is ⚠️ **DEPRECATED** ⚠️. We are working to migrate to the `@User` parameter decorator. The old method is working while the migration is in progress.
### Nota sobre decoradores de autenticação
- Authentication decorators can be used together and all of them **must** pass to the request be authenticated, but in the general case you don't need to (*and wouldn't like to...*) use all of them together, as you can code all of the authentication logic in the `@AuthenticateCondition` decorator.
- O método antigo de autenticação colocava os dados do usuário no campo `request.body.info`, o que impõe certos problemas em relação ao corpo da solicitação, pois esses dados devem vir do frontend sem qualquer modificação pelo Maestro. Agora, esse uso está ⚠️ **DESCONTINUADO** ⚠️. Estamos trabalhando para migrar para o decorador de parâmetro `@User`. O método antigo está funcionando enquanto a migração está em andamento.
```ts
import {
RequireAllPermissions,
RequireSomePermission,
AuthenticateCondition,
} from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { Waa, Baz } from './authenticationFunctions'
- Os decoradores de autenticação podem ser usados juntos e todos eles **devem** passar para que a solicitação seja autenticada, mas, no caso geral, você não precisa (_e não gostaria de..._) usar todos eles juntos, pois você pode codificar toda a lógica de autenticação no decorador `@AuthenticateCondition`.
@Controller('foo')
@RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST)
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN)
@AuthenticateCondition(Waa)
@AuthenticateCondition(Baz)
class FooController {
```ts
import {
RequireAllPermissions,
RequireSomePermission,
AuthenticateCondition,
} from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
import { Waa, Baz } from './authenticationFunctions';
@Controller('foo')
@RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST)
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN)
@AuthenticateCondition(Waa)
@AuthenticateCondition(Baz)
class FooController {
/* ... */
@Get('bar')
async getBar() {
/* ... */
@Get('bar')
async getBar() {
/* ... */
}
@Post('bar')
@RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.USE, Permissions.BAR.REQUEST)
async postBar() {
/* ... */
}
}
```
- If `@RequireAllPermissions` and `@RequireSomePermission` are used with **only a single permission**, they present the **exactly same behavior**.
@Post('bar')
@RequireAllPermissions(
Permissions.BAR.MANAGE,
Permissions.BAR.USE,
Permissions.BAR.REQUEST,
)
async postBar() {
/* ... */
}
}
```
```ts
// same behavior
@RequireAllPermissions(Permissions.BAR.MANAGE)
@RequireSomePermission(Permissions.BAR.MANAGE)
```
- Se `@RequireAllPermissions` e `@RequireSomePermission` forem usados com **apenas uma única permissão**, eles apresentam o **exatamente mesmo comportamento**.
```ts
// mesmo comportamento
@RequireAllPermissions(Permissions.BAR.MANAGE)
@RequireSomePermission(Permissions.BAR.MANAGE)
```
## Decorador de parâmetro `@User`
## `@User` parameter decorator
The requesting user data can be obtained using the @User parameter decorator, like in the following snippet:
Os dados do usuário solicitante podem ser obtidos usando o decorador de parâmetro @User, como no exemplo a seguir:
```ts
import { User, RequestUser } from '../../authentication/user.decorator';
@@ -237,7 +283,7 @@ class FooController {
}
```
If the user is required to be logged in, set `required` to `true`, as you would want in the `change-password` operation:
Se o usuário precisar estar logado, defina `required` como `true`, como por exemplo:
```ts
import { User, RequestUser } from '../../authentication/user.decorator';
@@ -263,39 +309,46 @@ class UserController {
}
```
## 📦 Development
### ⌨️ Coding Style
By default, we use [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) with default settings.
## 📦 Desenvolvimento
**We recommend using Visual Studio Code and installing the recommended extensions to ease the development process.**
### ⌨️ Estilo de Codificação
### Commits pattern
Our workflow pipeline follows the conventional commits specs ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) to release versions accordingly.
Por padrão, usamos [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) com configurações padrão.
Format: `<type>[optional scope]: <description>`
**Recomendamos usar o Visual Studio Code e instalar as extensões recomendadas para facilitar o processo de desenvolvimento.**
Example: `FIX: ensure Range headers adhere more closely to RFC 2616`
### Padrão de commits
### Branching naming convention
- **Feature**: Any code changes for a new module or use case should be done on a feature branch. This branch is created based on the `main` branch. When all changes are done, a Pull Request/Merge Request is needed to put all of these changes back to the `main` branch. Examples: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`.
Nosso pipeline de fluxo de trabalho segue as especificações de commits convencionais ([cheat sheets](https://cheatography.com/albelop/cheat-sheets/conventional-commits/)) para liberar versões conforme necessário.
**It is recommended to use all lower caps letters and hyphen (-) to separate words unless it is a specific item name or ID. Underscore (_) could be used to separate the ID and description.**
Formato: `<type>[optional scope]: <description>`
- **Bug Fix**: If the code changes made from the feature branch were rejected after a release, sprint or demo, any necessary fixes after that should be done on the bugfix branch. Examples: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
Exemplo: `FIX: ensure Range headers adhere more closely to RFC 2616`
- **Hot Fix**: If there is a need to fix a blocker, do a temporary patch, apply a critical framework or configuration change that should be handled immediately, it should be created as a Hotfix. Examples: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`.
### Convenção de nomenclatura de branchs
- **Experimental**: A branch for playing around. Any new feature or idea that is not part of a release or a sprint. Example: `experimental/dark-theme-support`.
- **Feature**: Quaisquer alterações de código para um novo módulo ou caso de uso devem ser feitas em uma branch de feature. Esta branch é criada com base na branch `main`. Quando todas as alterações estiverem concluídas, será necessário um Pull Request/Merge Request para colocar todas essas alterações de volta na branch `main`. Exemplos: `feature/integrate-swagger`, `feature/JIRA-1234`, `feature/JIRA-1234_support-dark-theme`.
### Making a Pull Request
1. Commit your changes
2. Open the Pull Request on GitHub
3. Send Pull Request link in microsfera Google Chat Group for review and possible approval
**Recomenda-se usar todas as letras em minúsculas e hífen (-) para separar palavras, a menos que seja um nome ou ID de item específico. O sublinhado (\_) pode ser usado para separar o ID e a descrição.**
## 🛠️ Built with
Some technologies used in this project:
- **Bug Fix**: Se as alterações de código feitas na branch de feature foram rejeitadas após um lançamento, sprint ou demo, quaisquer correções necessárias após isso devem ser feitas na branch de correção de bug. Exemplos: `bugfix/more-gray-shades`, `bugfix/JIRA-1444_gray-on-blur-fix`.
- [NestJS](https://docs.nestjs.com) - Framework for building efficient and scalable NodeJS server-side applications
- **Hot Fix**: Se houver necessidade de corrigir um bloqueador, fazer um patch temporário, aplicar uma mudança crítica de framework ou configuração que deva ser tratada imediatamente, ela deve ser criada como um Hotfix. Exemplos: `hotfix/disable-endpoint-zero-day-exploit`, `hotfix/increase-scaling-threshold`.
## ⚙️ Back-end Architecture
The architecture can be found at [this link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
- **Experimental**: Uma branch para experimentar. Qualquer nova feature ou ideia que não faça parte de um lançamento ou sprint. Exemplo: `experimental/dark-theme-support`.
### Fazendo um Pull Request
1. Comite suas alterações
2. Abra o Pull Request no GitHub
3. Envie o link do Pull Request no grupo de chat do Google da Microsfera para revisão e possível aprovação
## 🛠️ Construído com
Algumas tecnologias usadas neste projeto:
- [NestJS](https://docs.nestjs.com) - Framework para construir aplicações NodeJS eficientes e escaláveis no lado do servidor
## ⚙️ Arquitetura de Back-end
A arquitetura pode ser encontrada neste [link](https://sites.google.com/dadosfera.ai/wikidoproduto/time/back-end).
+4
View File
@@ -0,0 +1,4 @@
services:
maestro:
build: .
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
+1 -2
View File
@@ -1,5 +1,4 @@
version: "3.8"
services:
maestro:
build: .
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_${ENV}:${IMAGE_TAG}
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd:${IMAGE_TAG}
+28
View File
@@ -0,0 +1,28 @@
services:
maestro:
image: dadosfera/maestro_${ENV}:${IMAGE_TAG}
container_name: maestro
restart: always
ports:
- 3333:3333
env_file:
- .env
node_exporter:
image: ${NODE_EXPORTER_URL}
container_name: node_exporter
restart: always
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
- /run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
- '--collector.systemd'
- '--collector.processes'
network_mode: host
pid: host
+1 -2
View File
@@ -1,4 +1,3 @@
version: "3.8"
services:
maestro:
image: ${ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_${ENV}:${IMAGE_TAG}
@@ -10,7 +9,7 @@ services:
- .env
node_exporter:
image: prom/node-exporter:v1.3.1
image: ${NODE_EXPORTER_URL}
container_name: node_exporter
restart: always
volumes:
File diff suppressed because it is too large Load Diff
+9244
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'test';
ENV: 'local' | 'stg' | 'prd' | 'test';
LOCAL_ENV: 'stg' | 'prd';
DUC_URL: string;
INFACTORY_URL: string;
PIFACTORY_URL: string;
INTERNAL_SWAGGER: 'true' | 'false';
AWS_REGION: string;
OPEN_GROUP_ID: string;
OPEN_CUSTOMER_ID: string;
DEDICATED_PROXY: string;
}
}
}
export {};
+53
View File
@@ -0,0 +1,53 @@
charts:
- name: maestro
chart: ../maestro
values:
- ../maestro/values.yaml
set:
- name: app_name
value: maestro
- name: maestro.duc_url
value: duc.dadosfera.ai
- name: hostname
value: maestro.dadosfera.ai
- name: maestro.pi_factory_url
value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url
value: in-factory.dadosfera.ai
- name: maestro.tr_factory_url
value: in-factory.dadosfera.ai
- name: maestro.open_customer_id
value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id
value: c0afdcce-c5be-40d0-9d1d-2d271121f14a
- name: replicaCount
value: 2
- name: unimed-maestro
chart: ../maestro
values:
- ../maestro/values.yaml
set:
- name: app_name
value: maestro-unimed
- name: maestro.duc_url
value: duc.dadosfera.ai
- name: hostname
value: maestro-unimed.dadosfera.ai
- name: maestro.pi_factory_url
value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url
value: in-factory.dadosfera.ai
- name: maestro.tr_factory_url
value: in-factory.dadosfera.ai
- name: maestro.open_customer_id
value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id
value: c0afdcce-c5be-40d0-9d1d-2d271121f14a
# Customer id
- name: maestro.dedicated_proxy
value: dea2c27f-0973-4588-a2e0-9e31b64c7ffd
- name: replicaCount
value: 1
- name: maestro.restricted_ip
value: "177.52.172.0/24,189.84.160.157/32,186.237.171.146/32,57.151.113.140/30"
+52
View File
@@ -0,0 +1,52 @@
charts:
- name: maestro
chart: ../maestro
values:
- ../maestro/values.yaml
set:
- name: maestro.duc_url
value: duc.stg.dadosfera.ai
- name: hostname
value: maestro.stg.dadosfera.ai
- name: maestro.pi_factory_url
value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url
value: in-factory.stg.dadosfera.ai
- name: maestro.tr_factory_url
value: in-factory.dadosfera.ai
- name: maestro.open_customer_id
value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id
value: e3f98a2f-7748-4981-8505-7695c8ca8218
- name: replicaCount
value: 1
# Environment to test Network Policies
- name: private-maestro
chart: ../maestro
values:
- ../maestro/values.yaml
set:
- name: app_name
value: maestro-private
- name: maestro.duc_url
value: duc.stg.dadosfera.ai
- name: hostname
value: private-maestro.stg.dadosfera.ai
- name: maestro.pi_factory_url
value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url
value: in-factory.stg.dadosfera.ai
- name: maestro.tr_factory_url
value: in-factory.dadosfera.ai
- name: maestro.open_customer_id
value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id
value: e3f98a2f-7748-4981-8505-7695c8ca8218
# Customer id
- name: maestro.dedicated_proxy
value: 14d52fd4-d83d-4cdd-be34-bf11cc28b3bd
- name: replicaCount
value: 1
- name: maestro.restricted_ip
value: "57.151.113.140/30"
+23
View File
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
+24
View File
@@ -0,0 +1,24 @@
apiVersion: v2
name: maestro
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"
View File
+130
View File
@@ -0,0 +1,130 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.app_name }}
namespace: applications
labels:
app: {{ .Values.app_name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Values.app_name }}
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: {{ .Values.app_name }}
spec:
imagePullSecrets:
- name: {{ .Values.imagePullSecrets }}
nodeSelector:
"beta.kubernetes.io/os": linux
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: application
operator: In
values:
- general
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
containers:
- name: maestro
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
ports:
- containerPort: {{ .Values.containerPort }}
resources:
requests:
cpu: {{ .Values.resources.requests.cpu }}
memory: {{ .Values.resources.requests.memory }}
limits:
cpu: {{ .Values.resources.limits.cpu }}
memory: {{ .Values.resources.limits.memory }}
env:
- name: AWS_IDENTITY_POOL_ID
value: {{ .Values.maestro.aws_identity_pool_id }}
- name: AWS_REGION
value: "us-east-1"
- name: BASE_HOST
value: "maestro_prd"
- name: BUCKET_CUSTOMER_CSV_ASSETS
value: {{ .Values.maestro.bucket_customer_csv_assets }}
- name: CONNECTORS_INDEX
value: "connectors"
- name: DUC_URL
value: {{ .Values.maestro.duc_url }}
- name: ELASTIC_APM_ENVIRONMENT
value: {{ .Values.maestro.env }}
- name: ELASTIC_APM_SERVER_URL
value: "https://apm-server.dadosfera.ai"
- name: ELASTIC_APM_SERVICE_NAME
value: {{ .Values.maestro.apm_service }}
- name: ENV
value: {{ .Values.maestro.env }}
- name: INFACTORY_URL
value: {{ .Values.maestro.in_factory_url }}
- name: LOGGER_GELF_HOST
value: "logstash-pipelines.dadosfera.ai"
- name: LOGGER_GELF_PORT
value: "{{ .Values.maestro.logger_gelf_port }}"
- name: NIMBUS_BASE_URL
value: "http://nimbus-api"
- name: NPM_TOKEN
value: {{ .Values.maestro.npm_token }}
- name: PB_TOKEN_PATH
value: {{ .Values.maestro.pb_token_path }}
- name: PIFACTORY_URL
value: {{ .Values.maestro.pi_factory_url }}
- name: SM_OAUTH_PATH
value: {{ .Values.maestro.sm_oauth_path }}
- name: TRFACTORY_URL
value: {{ .Values.maestro.tr_factory_url }}
- name: 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: DEDICATED_PROXY
value: {{ .Values.maestro.dedicated_proxy }}
- name: REDIS_DATABASE
value: "{{ .Values.maestro.redis_database }}"
- name: REDIS_HOST
value: {{ .Values.maestro.redis_host }}
- name: REDIS_PORT
value: "{{ .Values.maestro.redis_port }}"
- name: JWT_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: prd-duc
key: jwt_token
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: prd-{{ .Values.app_name }}
key: AWS_ACCESS_KEY_ID
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: prd-{{ .Values.app_name }}
key: AWS_SECRET_ACCESS_KEY
- name: AWS_DEFAULT_REGION
valueFrom:
secretKeyRef:
name: prd-{{ .Values.app_name }}
key: AWS_DEFAULT_REGION
+32
View File
@@ -0,0 +1,32 @@
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: {{ .Values.app_name }}
{{- if .Values.maestro.dedicated_proxy}}
name: open-data-{{ .Values.app_name }}
{{- else }}
name: open-data
{{- end }}
namespace: applications
spec:
ingressClassName: nginx
rules:
- host: {{ .Values.hostname }}
http:
paths:
- backend:
service:
name: {{ .Values.app_name }}
port:
number: {{ .Values.ingress.port }}
path: /open-data/sharing-ocean-data
pathType: Prefix
+33
View File
@@ -0,0 +1,33 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/server-snippet: |
underscores_in_headers on;
ignore_invalid_headers on;
{{- if .Values.maestro.restricted_ip}}
nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.maestro.restricted_ip }}
{{- end }}
generation: 1
labels:
app: {{ .Values.app_name }}
name: {{ .Values.app_name }}
namespace: applications
spec:
ingressClassName: nginx
rules:
- host: {{ .Values.hostname }}
http:
paths:
- backend:
service:
name: {{ .Values.app_name }}
port:
number: {{ .Values.ingress.port }}
path: /
pathType: Prefix
+40
View File
@@ -0,0 +1,40 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: prd-{{ .Values.app_name }}
namespace: applications
labels:
app: {{ .Values.app_name }}
spec:
refreshInterval: 1h
secretStoreRef:
name: secretsmanager-prd
kind: SecretStore
target:
name: prd-{{ .Values.app_name }}
creationPolicy: Owner
data:
- secretKey: AWS_ACCESS_KEY_ID
remoteRef:
key: prd/microservices/aws_credentials/maestro
version: "AWSCURRENT"
property: AWS_ACCESS_KEY_ID
- secretKey: AWS_SECRET_ACCESS_KEY
remoteRef:
key: prd/microservices/aws_credentials/maestro
version: "AWSCURRENT"
property: AWS_SECRET_ACCESS_KEY
- secretKey: AWS_DEFAULT_REGION
remoteRef:
key: prd/microservices/aws_credentials/maestro
version: "AWSCURRENT"
property: AWS_DEFAULT_REGION
- secretKey: jwt_token
remoteRef:
key: {{ .Values.maestro.jwt_token_secret_id }}
version: "AWSCURRENT"
property: token
+18
View File
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.app_name }}
namespace: applications
labels:
app: {{ .Values.app_name }}
spec:
type: ClusterIP
ports:
- name: {{ .Values.app_name }}
protocol: TCP
port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
selector:
app: {{ .Values.app_name }}
+56
View File
@@ -0,0 +1,56 @@
# Default values for metabase.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 3
hostname: maestro.dadosfera.ai
image:
repository: 611330257153.dkr.ecr.us-east-1.amazonaws.com/microservices/maestro_prd
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: 1.56.0
app_name: maestro
containerPort: 3333
imagePullSecrets: "applications-secrets-ecr-auth-token-external-secret"
service:
type: ClusterIP
port: 3333
targetPort: 3333
ingress:
enabled: false
port: 3333
resources:
requests:
cpu: 100m
memory: 1500Mi
limits:
cpu: 2000m
memory: 2Gi
maestro:
aws_identity_pool_id: "us-east-1_Mrezsw9Sn"
duc_url: duc.dadosfera.ai
in_factory_url: in-factory.dadosfera.ai
bucket_customer_csv_assets: "customers-csv-assets-prd-611330257153"
env: prd
apm_service: maestro
jwt_token_secret_id: prd/root/jwt_token
logger_gelf_port: "1026"
npm_token: npm_Xp17h3daMkORcZ55NY95Ez4gRfdzsQ3UvINP
pb_token_path: prd/root/productboard_token
pi_factory_url: pi-factory.dadosfera.ai
sm_oauth_path: prd/root/oauth_applications
tr_factory_url: in-factory.dadosfera.ai
upload_file_agent_connection: cbc2f881-58c4-4d60-8003-0979b0b5b911
open_customer_id: f239718a-a271-4ef9-ae7e-02a2f0f3aa6e
open_group_id: 401573bb-334f-44b2-b30e-88d4cea31ae9
dedicated_proxy: ""
restricted_ip: ""
redis_host: "product-redis-prd.z4xvqj.0001.use1.cache.amazonaws.com"
redis_port: "6379"
redis_database: "0"
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
+1
View File
@@ -4,6 +4,7 @@
"compilerOptions": {
"assets": [
"**/*.proto",
"assets/**/*",
{
"include": "i18n/**/*",
"watchAssets": true
+6162 -11032
View File
File diff suppressed because it is too large Load Diff
+58 -46
View File
@@ -6,91 +6,103 @@
"private": true,
"license": "UNLICENSED",
"engines": {
"node": "18.3.0"
"node": "18.17"
},
"scripts": {
"preinstall": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1",
"co:login": "aws codeartifact login --tool npm --namespace @dadosfera --repository dadosfera-npm --domain dadosfera --domain-owner 611330257153 --region us-east-1",
"proto-update": "npm i @dadosfera/protospack-v2@latest --save-exact",
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:dev": "export INTERNAL_SWAGGER=true && nest start --watch",
"start:debug": "npm run start:dev",
"start:prod": "node dist/main",
"docs": "export KILL_AFTER_START=1 && nest start && export INTERNAL_SWAGGER=true && nest start",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test": "jest --detectOpenHandles --forceExit",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.112.0",
"@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack-v2": "3.18.10",
"@grpc/grpc-js": "^1.6.7",
"@grpc/proto-loader": "^0.6.13",
"@nestjs/common": "^8.4.7",
"@nestjs/config": "^1.2.1",
"@nestjs/core": "^8.4.7",
"@nestjs/mapped-types": "*",
"@nestjs/microservices": "^8.4.7",
"@nestjs/passport": "^8.2.2",
"@nestjs/platform-express": "^8.4.7",
"@nestjs/schedule": "^1.1.0",
"@nestjs/swagger": "^5.2.1",
"@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.38.0-beta.10",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
"@nestjs/common": "^9.4.3",
"@nestjs/config": "^2.3.4",
"@nestjs/core": "^9.4.3",
"@nestjs/mapped-types": "^1.2.2",
"@nestjs/microservices": "^9.4.3",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.4.3",
"@nestjs/schematics": "^9.2.0",
"@nestjs/swagger": "^6.3.0",
"@nestjs/testing": "^9.4.3",
"axios": "^0.27.2",
"cache-manager": "^5.1.4",
"cache-manager-ioredis-yet": "^1.1.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"cron-parser": "^4.4.0",
"class-validator": "^0.14.0",
"cron-parser": "^4.9.0",
"csv": "^6.3.11",
"dotenv": "^14.3.2",
"elastic-apm-node": "^3.36.0",
"helmet": "^5.1.0",
"jsonwebtoken": "^8.5.1",
"elastic-apm-node": "^3.50.0",
"handlebars": "^4.7.8",
"helmet": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"jwk-to-pem": "^2.0.5",
"mixpanel": "^0.17.0",
"ms": "^3.0.0-canary.1",
"openid-client": "^5.7.1",
"passport": "^0.6.0",
"passport-facebook": "^3.0.0",
"passport-forcedotcom": "^0.1.5",
"passport-forcedotcom": "^0.2.0",
"passport-google-oauth20": "^2.0.0",
"passport-hubspot-oauth2": "^1.0.3",
"passport-mailchimp": "^1.1.0",
"protospack": "2.5.2",
"puppeteer": "^24.7.2",
"redis": "^4.5.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.5",
"swagger-ui-express": "^4.4.0"
"swagger-ui-express": "^4.6.3"
},
"overrides": {
"multer": "1.4.5-lts.1"
},
"devDependencies": {
"@nestjs/cli": "^8.2.8",
"@nestjs/schematics": "^8.0.11",
"@nestjs/testing": "^8.4.7",
"@types/express": "^4.17.13",
"@types/cache-manager": "^4.0.6",
"@types/express": "^4.17.17",
"@types/express-session": "^1.18.1",
"@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.8",
"@types/jsonwebtoken": "^8.5.9",
"@types/jwk-to-pem": "^2.0.1",
"@types/multer": "^1.4.7",
"@types/node": "^16.11.41",
"@types/multer": "^1.4.12",
"@types/node": "^16.18.52",
"@types/passport-facebook": "^2.1.11",
"@types/passport-google-oauth20": "^2.0.11",
"@types/passport-oauth2": "^1.4.11",
"@types/passport-oauth2": "^1.4.12",
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"eslint": "^8.49.0",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^27.5.1",
"nock": "^13.2.7",
"prettier": "^2.7.1",
"nock": "^13.3.3",
"prettier": "^2.8.8",
"source-map-support": "^0.5.20",
"supertest": "^6.2.3",
"supertest": "^6.3.3",
"ts-jest": "^27.1.5",
"ts-loader": "^9.3.1",
"ts-node": "^10.8.1",
"tsconfig-paths": "^3.14.1",
"typescript": "^4.6.3"
"ts-loader": "^9.4.4",
"ts-node": "^10.9.1",
"tsconfig-paths": "^3.14.2",
"typescript": "^4.9.5"
}
}
+20 -9
View File
@@ -24,9 +24,14 @@ import { CatalogModule } from './modules/catalog/catalog.module';
import { ConfigModule } from '@nestjs/config';
import { PipelinesV2Module } from './modules/pipelinesV2/pipelines.module';
import { ProductboardModule } from './modules/productboard/productboard.module';
import { MixpanelModule } from './modules/mixpanel/mixpanel.module';
import { CustomersModule } from './modules/customers/customers.module';
import { OpenDataModule } from './modules/open-data/open-data.module';
import { ThemeModule } from './modules/theme/theme.module';
import { IdentityProviderModule } from './modules/identity-provider/identity-provider.module';
import { NetworkPolicyModule } from './modules/network-policy/network-policy.module';
@Module({
controllers: [],
providers: [
DadosferaLogger,
{
@@ -38,24 +43,30 @@ import { ProductboardModule } from './modules/productboard/productboard.module';
ConfigModule.forRoot({
isGlobal: true,
}),
ConnectorModule,
AuthModule,
PermissionsModule,
TermsOfUseModule,
ConnectionModule,
NetworkConfigModule,
InputsModule,
PipelinesV2Module,
CatalogModule,
ConnectorModule,
PermissionsModule,
TermsOfUseModule,
ConnectionTestModule,
PipelinesModule,
TransformationsModule,
HealthModule,
CatalogModule,
UsersModule,
RolesModule,
InputsModule,
OauthModule,
PipelinesV2Module,
CatalogModule,
ProductboardModule,
MixpanelModule,
CustomersModule,
OpenDataModule,
ThemeModule,
//Always leave HealthModule last, so it is on the bottom of swagger
HealthModule,
IdentityProviderModule,
NetworkPolicyModule
],
})
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>
@@ -1,56 +0,0 @@
import { Request } from 'express';
import { CustomDecorator } from '@nestjs/common';
import { PermissionsObjectPermission } from './permissions.enum';
import { RequestUser } from './user.decorator';
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
export type AuthenticationFunction = (req: Request, user: any) => boolean;
// implementation copied from SetMetadata, but tweaked to get existing values
// of the metadataKey and accumulate it with the new metadataValue
function SetMultipleMetadata(metadataKey, metadataValue): CustomDecorator {
const decoratorFactory = (target, key, descriptor) => {
// .start: tweak
// descriptor?.value = function decorator; target = class decorator
const accumulatedVal =
Reflect.getMetadata(metadataKey, descriptor?.value ?? target) ?? [];
accumulatedVal.push(metadataValue);
// .end: tweak
if (descriptor) {
Reflect.defineMetadata(metadataKey, accumulatedVal, descriptor.value);
return descriptor;
}
Reflect.defineMetadata(metadataKey, accumulatedVal, target);
return target;
};
decoratorFactory.KEY = metadataKey;
return decoratorFactory as any;
}
export function RequireAllPermissions(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireSomePermission(
...permissions: PermissionsObjectPermission[]
) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, (req, user: RequestUser) =>
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, func);
}
export function Authenticated() {
return SetMultipleMetadata(AUTH_FUNCTION_KEY, () => true);
}
@@ -11,7 +11,7 @@ import {
Authenticated,
RequireAllPermissions,
RequireSomePermission,
} from './authentication.decorator';
} from '../decorators/authentication.decorator';
import { AuthenticationGuard } from './authentication.guard';
import { PERMISSIONS_GROUPS } from './permissions.enum';
import { AuthClientService } from '../modules/auth/auth.service';
@@ -44,7 +44,7 @@ class NoClassAuthController {
@Get('has-all-permissions')
@RequireAllPermissions(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
)
async hasAllPermissions(@Body() body) {
return { body };
@@ -53,7 +53,7 @@ class NoClassAuthController {
@Get('has-some-permission')
@RequireSomePermission(
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
)
async hasSomePermission(@Body() body) {
return { body };
@@ -62,7 +62,7 @@ class NoClassAuthController {
@Controller('class-auth-condition')
@AuthenticateCondition((req) => req.get('x-on-class') === 'ok')
@RequireSomePermission(PERMISSIONS_GROUPS.METABASE.permissions.OPEN)
@RequireSomePermission(PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE)
class ClassAuthConditionController {
@Get('body')
async getBody(@Body() body) {
@@ -99,6 +99,7 @@ describe('authentication.guard', () => {
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
customer_name: 'dadosfera',
customer_tier: 'BASIC',
customer_modules: []
};
beforeAll(async () => {
@@ -444,13 +445,13 @@ describe('authentication.guard', () => {
NoClassAuthTest(tokenZ, ['zendesk']);
ClassAuthConditionTest(tokenZ, ['zendesk']);
const tokenM = CreateToken([PERMISSIONS_GROUPS.METABASE.permissions.OPEN]);
const tokenM = CreateToken([PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE]);
NoClassAuthTest(tokenM, ['metabase']);
ClassAuthConditionTest(tokenM, ['metabase']);
const tokenZM = CreateToken([
PERMISSIONS_GROUPS.ZENDESK.permissions.OPEN,
PERMISSIONS_GROUPS.METABASE.permissions.OPEN,
PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE,
]);
NoClassAuthTest(tokenZM, ['zendesk', 'metabase']);
ClassAuthConditionTest(tokenZM, ['zendesk', 'metabase']);
+16 -2
View File
@@ -4,6 +4,7 @@ import {
OnApplicationBootstrap,
ExecutionContext,
Inject,
ForbiddenException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import assert from 'assert';
@@ -13,8 +14,8 @@ import { AuthClientService } from '../modules/auth/auth.service';
import {
AuthenticationFunction,
AUTH_FUNCTION_KEY,
} from './authentication.decorator';
import { RequestUser } from './user.decorator';
} from '../decorators/authentication.decorator';
import { RequestUser } from '../decorators/user.decorator';
import ErrorBuilder from '../utils/ErrorBuilder';
import ErrorCodes from '../utils/errorCodes';
@@ -113,6 +114,18 @@ export class AuthenticationGuard
return false;
}
// Bloquear outros customer de usar o maestor dedicado
const DEDICATED_PROXY = process.env.DEDICATED_PROXY || '';
if (DEDICATED_PROXY !== '' && DEDICATED_PROXY !== accessTokenPayload.customer_id) {
throw new ErrorBuilder(ErrorCodes.AUTH.FORBIDDEN);
}
// Bloquear o customer de acesso o maestro publico
const hasNetworkPolicyModule = accessTokenPayload.customer_modules.includes('network-policy');
if (hasNetworkPolicyModule && DEDICATED_PROXY === '') {
throw new ForbiddenException(ErrorCodes.AUTH.FORBIDDEN);
}
request.accessTokenPayload = accessTokenPayload;
request.user = {
user_id: accessTokenPayload.user_id,
@@ -121,6 +134,7 @@ export class AuthenticationGuard
customer_id: accessTokenPayload.customer_id,
customer_name: accessTokenPayload.customer_name,
customer_tier: accessTokenPayload.customer_tier,
customer_modules: accessTokenPayload.customer_modules,
access_token: accessToken,
};
// TODO: for backwards compatibility. remove in the future
+155 -47
View File
@@ -55,9 +55,18 @@ export const PERMISSIONS_GROUPS = {
'es-es': '',
},
},
GENERATE_TOKEN: {
seqid: 46,
claim: 'customer:generate-token',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Gerar Token de Acesso',
'en-us': 'Generate Acess Token',
'es-es': 'Gerar Token de Acceso',
},
},
},
},
PIPELINE: {
title: {
'pt-br': 'Coletar | Pipelines',
@@ -107,7 +116,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
CONNECTION: {
title: {
'pt-br': 'Coletar | Fontes de dados',
@@ -128,11 +136,11 @@ export const PERMISSIONS_GROUPS = {
DELETE: {
seqid: 38,
claim: 'connection:delete',
usage: PermissionUsages.INTERNAL,
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Excluir Fonte',
'en-us': 'Remove Source',
'es-s': 'Eliminar Source',
'es-es': 'Eliminar Source',
},
},
UPDATE: {
@@ -147,7 +155,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
NETWORK_CONFIG: {
title: {
'pt-br': 'Coletar | Redes',
@@ -177,7 +184,6 @@ export const PERMISSIONS_GROUPS = {
// },
},
},
OUTPUT: {
title: {
'pt-br': 'Output',
@@ -227,7 +233,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
TRANSFORMATIONS: {
title: {
'pt-br': 'Micro-transformações',
@@ -277,7 +282,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
CATALOG: {
title: {
'pt-br': 'Explorar | Catálogo',
@@ -290,54 +294,74 @@ export const PERMISSIONS_GROUPS = {
claim: 'GET /catalog',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Buscar data assets',
'en-us': 'Search for data assets',
'es-es': 'Buscar data assets',
'pt-br': 'Acessar lista de Ativos e visualizar atributos',
'en-us': 'Access Assets list and its attributes',
'es-es': 'Acceda a la lista de Activos y vea los atributos',
},
},
CREATE: {
seqid: 19,
claim: 'POST /catalog',
claim: 'catalog:create',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Criar atributos no catálogo',
'en-us': 'Create attributes',
'es-es': 'Crear atributos',
'pt-br': 'Criar Ativos',
'en-us': 'Create Assets',
'es-es': 'Crear Activos',
},
},
UPDATE: {
seqid: 25,
claim: 'PUT /catalog',
usage: PermissionUsages.INTERNAL,
claim: 'catalog:edit',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Editar atributos no catálogo',
'en-us': 'Edit attributes',
'es-es': 'Editar atributos',
'pt-br': 'Criar e editar atributos no catálogo',
'en-us': 'Create and edit attributes on the catalog',
'es-es': 'Crear y editar atributos en el catálogo',
},
},
DELETE: {
seqid: 1,
claim: 'DELETE /catalog',
usage: PermissionUsages.INTERNAL,
claim: 'catalog:delete',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Excluir atributos no catálogo',
'en-us': 'Remove attributes',
'es-es': 'Eliminar atributos',
'pt-br': 'Excluir Ativos do catálogo',
'en-us': 'Remove Assets from the catalog',
'es-es': 'Eliminar Activos del catálogo',
},
},
DATA_MANAGER: {
seqid: 42,
claim: 'catalog:data_manager',
usage: PermissionUsages.PUBLIC,
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Vizualizar todos os recursos do catálogo.',
'en-us': 'View all data assets.',
'es-es': 'Ver todos los activos de datos',
'pt-br':
'Gerente do catálogo. Consegue vizualizar e editar todos os ativos.',
'en-us': 'Catalog manager. Able to view and edit all assets.',
'es-es': 'Gestor de catálogos. Puede ver y editar todos los activos.',
},
},
EMBED_ANALYTICS: {
seqid: 44,
claim: 'catalog:embed',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Módulo de Incorporação de Ativos',
'en-us': 'Access Embedding analytics Module',
'es-es': 'Acceder al Módulo de Incorporación de Activos',
},
},
TRIGGER_CATALOG_TASK: {
seqid: 45,
claim: 'catalog:trigger-task',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Executar a catalogação de um ativo',
'en-us': 'Trigger an asset cataloging',
'es-es': 'Realizar el listado de un activo',
},
},
},
},
CONNECTORS: {
title: {
'pt-br': 'Conectores',
@@ -387,7 +411,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
SNOWFLAKE: {
title: {
'pt-br': 'Explorar | Consolidar',
@@ -407,7 +430,6 @@ export const PERMISSIONS_GROUPS = {
},
},
},
ZENDESK: {
title: {
'pt-br': 'Zendesk',
@@ -417,25 +439,24 @@ export const PERMISSIONS_GROUPS = {
permissions: {
OPEN: {
seqid: 16,
claim: 'GET /zendesk',
claim: 'support-chat:access',
usage: PermissionUsages.INTERNAL,
name: {
'pt-br': 'Acessar Zendesk',
'en-us': 'Access Zendesk',
'es-es': 'Acceso Zendesk',
'pt-br': 'Acessar chat de suporte',
'en-us': 'Access support chat',
'es-es': 'Acceder al chat de soporte',
},
},
},
},
METABASE: {
DATAVIZ: {
title: {
'pt-br': 'Analisar | Visualização',
'en-us': 'Analyze | Visualization',
'es-es': 'Analizar | Visualización',
},
permissions: {
OPEN: {
METABASE: {
seqid: 11,
claim: 'GET /metabase',
usage: PermissionUsages.PUBLIC,
@@ -447,8 +468,7 @@ export const PERMISSIONS_GROUPS = {
},
},
},
ORCHEST: {
INTELLIGENCE: {
title: {
'pt-br': 'Analisar | Inteligência',
'en-us': 'Analyze | Intelligence',
@@ -458,13 +478,22 @@ export const PERMISSIONS_GROUPS = {
INTELLIGENCE: {
seqid: 31,
claim: 'intelligence:open',
usage: PermissionUsages.INTERNAL,
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Orchest',
'en-us': 'Access Orchest',
'es-s': 'Acceso Orchest',
'pt-br': 'Acessar Módulo de Inteligência',
'en-us': 'Access Intelligence Module',
'es-s': 'Acceder Módulo de Inteligencia',
},
},
},
},
MODULES: {
title: {
'pt-br': 'Módulos da Dadosfera',
'en-us': 'Dadosfera Modules',
'es-es': 'Módulos de la Dadosfera',
},
permissions: {
APP_BUILDER: {
seqid: 32,
claim: 'app-builder:open',
@@ -472,7 +501,7 @@ export const PERMISSIONS_GROUPS = {
name: {
'pt-br': 'Acessar App Builder',
'en-us': 'Access App Builder',
'es-es': 'Acceso App Builder',
'es-es': 'Acceder App Builder',
},
},
MACHINE_LEARNING: {
@@ -482,7 +511,7 @@ export const PERMISSIONS_GROUPS = {
name: {
'pt-br': 'Acessar Machine Learning',
'en-us': 'Access Machine Learning',
'es-es': 'Acceso Machine Learning',
'es-es': 'Acceder Machine Learning',
},
},
},
@@ -506,6 +535,25 @@ export const PERMISSIONS_GROUPS = {
},
},
},
PROCESS: {
title: {
'pt-br': 'Processar',
'en-us': 'Process',
'es-es': 'Procesar',
},
permissions: {
TRANSFORMATION: {
seqid: 43,
claim: 'process:open',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Acessar Módulo de Transformação',
'en-us': 'Access Transformation Module',
'es-es': 'Acceder Módulo de transformación',
},
},
},
},
DADOSFERA: {
title: {
'pt-br': 'Usuários',
@@ -535,8 +583,63 @@ export const PERMISSIONS_GROUPS = {
},
},
},
CUSTOMER: {
title: {
'pt-br': 'Organização',
'en-us': 'Organization',
'es-es': 'Organización',
},
permissions: {
MONITORING_DASHBOARD: {
seqid: 47,
claim: 'customer:monitoring-dashboard',
usage: PermissionUsages.PUBLIC,
name: {
'pt-br': 'Ver o dashboard de monitoramento',
'en-us': 'View the monitoring dashboard',
'es-es': 'Ver el dashboard de monitoreo',
},
},
},
},
};
export interface DadosferaModule {
name: string;
description: string;
key: string;
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> = [
{
name: 'Intelligence Module',
description: 'Orchest Module',
key: 'intelligence',
permissionSeqId:
PERMISSIONS_GROUPS.INTELLIGENCE.permissions.INTELLIGENCE.seqid,
},
{
name: 'Proccessing Module',
description: 'Proccessing Module',
key: 'process',
permissionSeqId:
PERMISSIONS_GROUPS.PROCESS.permissions.TRANSFORMATION.seqid,
},
{
name: 'Embedded Analytics',
description: 'Embedded Analytics Module',
key: 'embedded-analytics',
permissionSeqId:
PERMISSIONS_GROUPS.PROCESS.permissions.TRANSFORMATION.seqid,
},
];
// traverses the object searching for duplicate seqids or claims (executes at runtime)
let nextAvailableSeqid = 0;
const seqids = Object.values(PERMISSIONS_GROUPS).flatMap((namespace) =>
@@ -556,4 +659,9 @@ Object.values(PERMISSIONS_GROUPS).map((namespace) =>
}),
);
DADOSFERA_MODULES.map((m) => m.key).forEach((m, i, arr) => {
if (arr.indexOf(m) !== i)
throw new Error(`DADOSFERA_MODULES[${i}] does not have a unique key`);
});
logger.log(`next available seqid ${nextAvailableSeqid + 1}`);
@@ -0,0 +1,49 @@
import { applyDecorators } from '@nestjs/common';
import { ApiSecurity } from '@nestjs/swagger';
import { type Request } from 'express';
import { type PermissionsObjectPermission } from '../authentication/permissions.enum';
import { type RequestUser } from './user.decorator';
import { SetMultipleMetadata } from './shared';
export const AUTH_FUNCTION_KEY = '__AUTH_FUNCTION__';
export type AuthenticationFunction = (req: Request, user: any) => boolean;
export function RequireAllPermissions(
...permissions: PermissionsObjectPermission[]
) {
return createAuthenticatedDecorator((req, user: RequestUser) =>
permissions.every(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireSomePermission(
...permissions: PermissionsObjectPermission[]
) {
return createAuthenticatedDecorator((req, user: RequestUser) =>
permissions.some(({ seqid }) => user.permissions.includes(seqid)),
);
}
export function RequireModule(
key: string
) {
return createAuthenticatedDecorator((_, user: RequestUser) =>
user.customer_modules.some(module => module === key),
);
}
export function AuthenticateCondition(func: AuthenticationFunction) {
return createAuthenticatedDecorator(func);
}
export function Authenticated() {
return createAuthenticatedDecorator(() => true);
}
function createAuthenticatedDecorator(metadataValue: AuthenticationFunction) {
return applyDecorators(
ApiSecurity('access-token'),
SetMultipleMetadata(AUTH_FUNCTION_KEY, metadataValue),
);
}
+25
View File
@@ -0,0 +1,25 @@
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
import { LanguageEnum } from 'src/utils/languages.enum';
export const Language: () => ParameterDecorator = createParamDecorator(
(options: any, ctx: ExecutionContext): LanguageEnum => {
const headers = ctx.switchToHttp()?.getRequest()?.headers;
if (!headers) return LanguageEnum.ptbr;
let language: LanguageEnum =
headers['dadosfera-lang'] ?? headers['accept-language'];
language = language?.toLowerCase().trim() as LanguageEnum;
if (!language) return LanguageEnum.ptbr;
Object.values(LanguageEnum).some((l) => {
if (language === l || l.includes(language)) {
language = l;
return true;
}
language = LanguageEnum.enus;
});
return language;
},
);
+28
View File
@@ -0,0 +1,28 @@
import { type CustomDecorator } from '@nestjs/common';
// implementation copied from SetMetadata, but tweaked to get existing values
// of the metadataKey and accumulate it with the new metadataValue
export function SetMultipleMetadata(
metadataKey,
metadataValue,
): CustomDecorator {
const decoratorFactory: CustomDecorator = (target, key?, descriptor?) => {
// .start: tweak
// descriptor?.value = function decorator; target = class decorator
const accumulatedVal =
Reflect.getMetadata(metadataKey, descriptor?.value ?? target) ?? [];
accumulatedVal.push(metadataValue);
// .end: tweak
if (descriptor) {
Reflect.defineMetadata(metadataKey, accumulatedVal, descriptor.value);
return descriptor;
}
Reflect.defineMetadata(metadataKey, accumulatedVal, target);
return target;
};
decoratorFactory.KEY = metadataKey;
return decoratorFactory;
}
+13
View File
@@ -0,0 +1,13 @@
import { applyDecorators } from '@nestjs/common';
import { ApiExcludeController, ApiExcludeEndpoint } from '@nestjs/swagger';
export function ApiInternalOnlyEndpoint() {
if (process.env.INTERNAL_SWAGGER !== 'true')
return applyDecorators(ApiExcludeEndpoint());
return () => null;
}
export function ApiInternalOnlyController() {
if (process.env.INTERNAL_SWAGGER !== 'true')
return applyDecorators(ApiExcludeController());
return () => null;
}
@@ -1,17 +1,14 @@
import request from 'supertest';
import { Test } from '@nestjs/testing';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import jwt from 'jsonwebtoken';
import { Controller, Get } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { AuthenticationGuard } from './authentication.guard';
import { Controller, Get, HttpStatus, INestApplication } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { Test } from '@nestjs/testing';
import jwt from 'jsonwebtoken';
import request from 'supertest';
import { AuthenticationGuard } from '../authentication/authentication.guard';
import { PERMISSIONS_GROUPS } from '../authentication/permissions.enum';
import { AuthClientService } from '../modules/auth/auth.service';
import ErrorCodes from '../utils/errorCodes';
import { User } from './user.decorator';
import { PERMISSIONS_GROUPS } from './permissions.enum';
const logger = {
info: (...args) => args,
@@ -49,12 +46,13 @@ describe('user.decorator', () => {
const fakeUserPayload = {
user_id: 'd50d33c7-6c2b-463c-861f-e21667e7c125',
username: 'super.admin',
permissions: [PERMISSIONS_GROUPS.METABASE.permissions.OPEN].map(
permissions: [PERMISSIONS_GROUPS.DATAVIZ.permissions.METABASE].map(
({ seqid }) => seqid,
),
customer_id: '9d18e8ae-24b9-41a3-9e8f-a25ce57555b11',
customer_name: 'dadosfera',
customer_tier: 'BASIC',
customer_modules: [],
access_token: '',
};
@@ -1,4 +1,4 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
import ErrorBuilder from '../utils/ErrorBuilder';
import ErrorCodes from '../utils/errorCodes';
@@ -11,6 +11,7 @@ export interface RequestUser {
customer_name: string;
customer_tier: string;
access_token: string;
customer_modules: string[];
}
export const User: (options?: { required?: boolean }) => ParameterDecorator =
@@ -3,7 +3,6 @@ import {
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { Response } from 'express';
+56 -14
View File
@@ -3,9 +3,13 @@ import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import helmet from 'helmet';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { json, urlencoded } from 'express';
import { AppModule } from './app.module';
import { writeFileSync } from 'fs';
import { execSync } from 'child_process';
import { INestApplication } from '@nestjs/common';
async function bootstrap() {
DadosferaLogger.setupLogger({
serviceName: 'maestro',
@@ -22,28 +26,66 @@ async function bootstrap() {
optionsSuccessStatus: 204,
},
});
app.use(helmet());
const date = new Date();
if (process.env.ENV === 'prd') {
app.use('/catalog/register-dataset', json({ limit: '10mb' }));
app.use('/catalog/register-dataset', urlencoded({ extended: true, limit: '10mb' }));
}
const config = new DocumentBuilder()
.setTitle('Maestro')
configureSwagger(app);
await app.listen(3333);
if (process.env.KILL_AFTER_START) await app.close();
}
function configureSwagger(app: INestApplication) {
let branchName = '';
try {
branchName = execSync('git rev-parse --abbrev-ref HEAD')
.toString()
.replace('\n', '')
.trim();
} catch (error) {
console.warn("Can't get branch name", error);
}
const swaggerTitle = branchName ? `Maestro - ${branchName}` : 'Maestro';
const swaggerConfig = new DocumentBuilder()
.setTitle(swaggerTitle)
.setDescription('Documentation for Maestro gateway')
.setVersion(
`${date.getDate()}.${
date.getMonth() + 1
}.${date.getFullYear()} - ${date.getHours()}:${date.getMinutes()}`,
)
.addBearerAuth()
.build();
.addSecurity('access-token', {
type: 'apiKey',
name: 'Authorization',
in: 'header',
})
.setDescription('This is the Maestro API');
if (process.env.INTERNAL_SWAGGER !== 'true')
swaggerConfig.addServer('https://maestro.dadosfera.ai', 'Dadosfera API');
const config = swaggerConfig.build();
const document = SwaggerModule.createDocument(app, config);
const swaggerJsonFile =
process.env.INTERNAL_SWAGGER === 'true'
? 'docsfera.json'
: 'docsfera.external.json';
if (process.env.ENV === 'local') SwaggerModule.setup('api', app, document);
await writeFileSync('swagger.txt', JSON.stringify(JSON.stringify(document)));
await app.listen(3333);
writeFileSync(
swaggerJsonFile,
JSON.stringify(
{
service: '${DOCS_API_TOKEN}',
pageId: '${DOCS_PAGE_ID}',
blockId: '${DOCS_BLOCK_ID}',
schema: document,
},
null,
2,
),
);
}
bootstrap();
+38
View File
@@ -0,0 +1,38 @@
# Auth
## SSO/oAuth
### Strategy
We are using [PassportJs](https://www.passportjs.org/) to handle oAuth authentications.
When the client (front end) makes a `GET /auth/oauth/{strategy}` Passport automatically redirects the user to the `strategy` login page. To do that we must configure and use a **Passport Strategy**. We must also have a callback route, conventionally `GET /auth/oauth/{strategy}/callback`, so the oAuth app can report the status of the user's login.
- If the oAuth is successfull we call DUC's `AuthOauthSignIn` request that gets the tokens from Cognito and saves them on cache temporarily under a key we call `session`. Duc returns that `session` to maestro which then redirects the user to our app login page with that `session` as a query param.
- If the oAuth login is not successfull for some reason or the user **does not** exist on DUC's database we redirect the user to our login page with an `error` and `error_description` as query params.
### Routes
So in order to have an SSO login, besides configuring the Strategy, we must have two routes for each Strategy, like in the example below:
```ts
@Get('oauth/google')
@UseGuards(AuthGuard('google-login'))
googleOauth() {
this.logger.info('/oauth/google');
return true;
}
@Get('oauth/google/callback')
@UseGuards(AuthGuard('google-login'))
@Redirect()
async googleOauthCallback(@Req() req) {
const { url, email, token, language = 'pt-br' } = await this.callback(req);
if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR');
return { url: url.href };
}
// ... Rest of the logic
return { url: url.href };
}
```
+274 -41
View File
@@ -7,33 +7,60 @@ import {
HttpStatus,
Inject,
UseFilters,
Get,
UseGuards,
Redirect,
Req,
Param,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
AuthSignInRequest,
AuthRefreshAccessTokenRequest,
ApiHeaders,
ApiOkResponse,
ApiSecurity,
ApiTags,
} from '@nestjs/swagger';
import {
AuthChangePasswordRequest,
AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest,
AuthEnableTotpMfaRequest,
AuthDisableTotpMfaRequest,
AuthVerifyTotpMfaRequest,
AuthVerifyTotpMfaRequest
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { RequireAllPermissions } from 'src/authentication/authentication.decorator';
import {
Authenticated,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { AuthClientService } from './auth.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { Metadata } from '@grpc/grpc-js';
import { RequestUser, User } from 'src/decorators/user.decorator';
import {
AuthRefreshAccessTokenReq,
AuthRefreshAccessTokenRes,
AuthSignInReq,
AuthSignInRes,
BulkEditRequest,
} from './dtos/login';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { AuthGuard } from '@nestjs/passport';
import { Request } from 'express';
import ErrorCodes, { OauthErrors } from 'src/utils/errorCodes';
import jwt from 'jsonwebtoken';
import { LanguageEnum } from 'src/utils/languages.enum';
import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('Auth')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@UseFilters(new GrpcToHttpExceptionFilter())
@Controller('auth')
export class AuthController {
logger: DadosferaLogger;
redirectUrl: string;
constructor(
@Inject(DadosferaLogger)
@@ -41,8 +68,51 @@ export class AuthController {
private authClient: AuthClientService,
) {
this.logger = dadosferaLogger.logger;
switch (process.env.ENV) {
case 'stg':
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/auth/login`;
break;
case 'prd':
this.redirectUrl = `https://app.dadosfera.ai/auth/login`;
break;
default:
this.redirectUrl = `http://localhost:4200/auth/login`;
}
}
@Post('sign-in')
@HttpCode(HttpStatus.OK)
async signIn(
@Body() { username, password, totp }: AuthSignInReq,
@Language() language: LanguageEnum,
): Promise<AuthSignInRes> {
this.logger.info('/auth - SignIn');
const metadata = PackTheMetadata({ language });
this.logger.info('metadata: ' + JSON.stringify(metadata.toJSON()));
return this.authClient.signIn({ username, password, totp }, metadata);
}
@Post('refresh-access-token')
@HttpCode(HttpStatus.OK)
@ApiOkResponse({ type: AuthRefreshAccessTokenRes })
async refreshAccessToken(
@Body() body: AuthRefreshAccessTokenReq,
@Language() language: LanguageEnum,
@Headers('origin') origin: string,
) {
this.logger.info('/auth - RefreshAccessToken');
const { refreshToken, userId } = body;
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({
language,
custom_host: frontHost,
});
return this.authClient.refreshAccessToken({ refreshToken, userId }, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('/sso/snowflake')
@RequireAllPermissions(PERMISSIONS_GROUPS.SNOWFLAKE.permissions.OPEN)
@HttpCode(HttpStatus.OK)
@@ -58,31 +128,7 @@ export class AuthController {
});
}
@Post('sign-in')
@HttpCode(HttpStatus.OK)
async signIn(
@Body() { username, password, totp }: AuthSignInRequest,
@Headers('Dadosfera-Lang') language: string,
) {
this.logger.info('/auth - SignIn');
const metadata = new Metadata();
metadata.add('language', language || 'pt-br');
return this.authClient.signIn({ username, password, totp }, metadata);
}
@Post('refresh-access-token')
@HttpCode(HttpStatus.OK)
async refreshAccessToken(
@Body() { refreshToken }: AuthRefreshAccessTokenRequest,
@Headers('Dadosfera-Lang') language: string,
) {
this.logger.info('/auth - RefreshAccessToken');
const metadata = new Metadata();
metadata.add('language', language || 'pt-br');
return this.authClient.refreshAccessToken({ refreshToken }, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('change-password')
@HttpCode(HttpStatus.OK)
async changePassword(
@@ -101,16 +147,24 @@ export class AuthController {
});
}
@ApiInternalOnlyEndpoint()
@Post('reset-password')
@HttpCode(HttpStatus.OK)
async resetPassword(@Body() body: AuthResetPasswordRequest) {
async resetPassword(
@Body() body: AuthResetPasswordRequest,
@Language() language: LanguageEnum,
) {
this.logger.info('/auth - reset-password');
const metadata = PackTheMetadata({
language: language,
});
const { username } = body;
return this.authClient.resetPassword({ username });
return this.authClient.resetPassword({ username }, metadata);
}
@ApiInternalOnlyEndpoint()
@Post('verify-reset-password-code')
@HttpCode(HttpStatus.OK)
async verifyResetPasswordCode(
@@ -123,20 +177,29 @@ export class AuthController {
return this.authClient.verifyResetPasswordCode({ username, code });
}
@ApiInternalOnlyEndpoint()
@Post('confirm-reset-password')
@HttpCode(HttpStatus.OK)
async confirmResetPassword(@Body() body: AuthConfirmResetPasswordRequest) {
async confirmResetPassword(
@Body() body: AuthConfirmResetPasswordRequest,
@Headers('origin') origin: string,
) {
this.logger.info('/auth - confirm-reset-password');
const frontHost = origin.replace(/^https?:\/\//, '');
const metadata = PackTheMetadata({ custom_host: frontHost });
const { username, code, newPassword } = body;
return this.authClient.confirmResetPassword({
username,
code,
newPassword,
});
return this.authClient.confirmResetPassword(
{
username,
code,
newPassword,
},
metadata,
);
}
@ApiInternalOnlyEndpoint()
@Post('enable-totp')
@HttpCode(HttpStatus.OK)
async enableTotpMFA(
@@ -151,6 +214,7 @@ export class AuthController {
return this.authClient.enableTotpMFA({ accessToken, password });
}
@ApiInternalOnlyEndpoint()
@Post('disable-totp')
@HttpCode(HttpStatus.OK)
async disableTotpMFA(
@@ -165,6 +229,7 @@ export class AuthController {
return this.authClient.disableTotpMFA({ accessToken, password });
}
@ApiInternalOnlyEndpoint()
@Post('dismiss-totp')
@HttpCode(HttpStatus.OK)
async dismissTotpMFA(@Headers() headers) {
@@ -175,6 +240,7 @@ export class AuthController {
return this.authClient.dismissTotpMFA({ accessToken });
}
@ApiInternalOnlyEndpoint()
@Post('verify-totp')
@HttpCode(HttpStatus.OK)
async verifyTotp(@Body() body: AuthVerifyTotpMfaRequest, @Headers() headers) {
@@ -185,4 +251,171 @@ export class AuthController {
return this.authClient.verifyTotp({ accessToken, totp });
}
@ApiInternalOnlyEndpoint()
@Authenticated()
@ApiSecurity('access-token')
@Get('verify-access-token')
verifyAccessToken() {
return { access_token_status: 'valid' };
}
@ApiInternalOnlyEndpoint()
@Get('session/:session')
getSession(@Param('session') session: string) {
return this.authClient.getSession(session);
}
@ApiInternalOnlyEndpoint()
@Get('oauth/google')
@UseGuards(AuthGuard('google-login'))
googleOauth() {
this.logger.info('/oauth/google');
return true;
}
@ApiInternalOnlyEndpoint()
@Get('oauth/google/callback')
@UseGuards(AuthGuard('google-login'))
@Redirect()
async googleOauthCallback(@Req() req) {
const { url, email, token, language = 'pt-br' } = await this.callback(req);
if (url.searchParams.get('error')) {
this.logger.error('/oauth/google - ERROR');
return { url: url.href };
}
await this.authClient
.oauthSignIn({
username: email,
token,
})
.then(({ session }) => {
this.logger.info('/oauth/google - SUCESS');
url.searchParams.set('session', session);
})
.catch((err) => {
this.logger.error('/oauth/google - ERROR');
let error = OauthErrors.INVALID_CREDENTIALS[language].error;
let error_description =
OauthErrors.INVALID_CREDENTIALS[language].error_description;
switch (err.details) {
case ErrorCodes.USER.NOT_FOUND:
error = OauthErrors.USER_NOT_FOUND[language].error;
error_description =
OauthErrors.USER_NOT_FOUND[language].error_description(email);
break;
case ErrorCodes.AUTH.UNAUTHORIZED:
error = OauthErrors.INVALID_SESSION[language].error;
error_description =
OauthErrors.INVALID_SESSION[language].error_description;
break;
}
url.searchParams.set('error', error);
url.searchParams.set('error_description', error_description);
return null;
});
return { url: url.href };
}
async callback(req: Request) {
const { error, state } = req.query;
const { authInfo } = req;
const url = new URL(this.redirectUrl);
let email, token, error_title, error_description;
let language: 'pt-br' | 'en-us' = 'pt-br';
const stateObject = jwt.verify(
state as string,
process.env.JWT_PRIVATE_KEY,
);
if (typeof stateObject != 'string') language = stateObject.language;
if (error || !authInfo) {
this.logger.error(error);
if (!authInfo) this.logger.error('No authInfo', { request: req });
error_title = OauthErrors.INVALID_CREDENTIALS[language].error;
error_description =
OauthErrors.INVALID_CREDENTIALS[language].error_description;
if (error) error_description += ` - [${error}]`;
} else {
const { accessToken } = authInfo as any;
const { _json: userInfo } = req.user as any;
email = userInfo.email;
token = accessToken;
}
if (error_title) {
url.searchParams.set('error', error_title);
url.searchParams.set('error_description', error_description);
}
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);
}
}
+8 -1
View File
@@ -6,12 +6,19 @@ import { AuthController } from './auth.controller';
import { AuthClientService } from './auth.service';
import { DucClient } from '../duc/client.config';
import { GoogleLoginStrategy } from './passport-strategies/google-strategy';
import { getOauthSecrets } from 'src/utils/OauthSecrets';
const client = new DucClient();
@Module({
imports: [ClientsModule.register([client.providerOptions])],
controllers: [AuthController],
providers: [AuthClientService, DadosferaLogger],
providers: [
AuthClientService,
DadosferaLogger,
GoogleLoginStrategy,
{ provide: 'OAUTH_SECRETS', useValue: getOauthSecrets() },
],
exports: [AuthClientService],
})
export class AuthModule {}
+150 -19
View File
@@ -1,10 +1,10 @@
import { OnModuleInit, Inject, Injectable } from '@nestjs/common';
import { OnModuleInit, Inject, Injectable, ForbiddenException } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { lastValueFrom } from 'rxjs';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { AuthProtoService as AuthServiceInterface } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { AuthProtoService as AuthServiceInterface, IdentityProviderProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import {
AuthSnowflakeSignInRequest,
AuthSignInRequest,
@@ -17,15 +17,22 @@ import {
AuthResetPasswordRequest,
AuthVerifyResetPasswordCodeRequest,
AuthConfirmResetPasswordRequest,
AuthSignInResponse,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { DucClient } from '../duc/client.config';
import { Metadata } from '@grpc/grpc-js';
import { BulkEditResponse } from './dtos/login';
@Injectable()
export class AuthClientService implements OnModuleInit {
logger: DadosferaLogger;
private authService: AuthServiceInterface;
private identityProviderService: IdentityProviderProtoService;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
@@ -38,6 +45,10 @@ export class AuthClientService implements OnModuleInit {
this.authService = this.grpcClient.getService<AuthServiceInterface>(
ProtoServices.AuthProtoService,
);
this.identityProviderService = this.grpcClient.getService<IdentityProviderProtoService>(
ProtoServices.IdentityProviderProtoService,
);
}
async getPublicKeys() {
@@ -52,25 +63,59 @@ export class AuthClientService implements OnModuleInit {
return lastValueFrom(this.authService.AuthSnowflakeSignIn(input));
}
checkDedicatedProxy({
customer
}: AuthSignInResponse) {
const DEDICATED_PROXY = process.env.DEDICATED_PROXY || '';
this.logger.info('SignIn - Setting customer ID for dedicated proxy: ' + DEDICATED_PROXY);
this.logger.info('Customer ID: ' + customer.id);
if (DEDICATED_PROXY !== '' && DEDICATED_PROXY !== customer.id) {
throw new ForbiddenException();
}
// Bloquear o customer de acesso o maestro publico
this.logger.info('Check if customer have network policy: ' + customer.modules);
const hasNetworkPolicyModule = customer.modules.includes('network-policy');
if (hasNetworkPolicyModule && DEDICATED_PROXY === '') {
throw new ForbiddenException();
}
}
async signIn(
{ username, password, totp }: AuthSignInRequest,
metadata: Metadata,
) {
this.logger.info('SignIn');
return lastValueFrom(
this.authService.AuthSignIn({ username, password, totp }, metadata),
);
let result: AuthSignInResponse;
try {
result = await lastValueFrom(
this.authService.AuthSignIn({ username, password, totp }, metadata),
);
} catch (error) {
this.logger.error('SignIn - Error during sign-in');
this.logger.error(error);
throw error;
}
if (result.customer) {
this.checkDedicatedProxy(result);
}
return result
}
async refreshAccessToken(
{ refreshToken }: AuthRefreshAccessTokenRequest,
{ refreshToken, userId }: AuthRefreshAccessTokenRequest,
metadata: Metadata,
) {
this.logger.info('RefreshAccessToken');
return lastValueFrom(
this.authService.AuthRefreshAccessToken({ refreshToken }, metadata),
this.authService.AuthRefreshAccessToken({ refreshToken, userId }, metadata),
);
}
@@ -90,10 +135,15 @@ export class AuthClientService implements OnModuleInit {
);
}
async resetPassword({ username }: AuthResetPasswordRequest) {
async resetPassword(
{ username }: AuthResetPasswordRequest,
metadata: Metadata,
) {
this.logger.info('resetPassword');
return lastValueFrom(this.authService.AuthResetPassword({ username }));
return lastValueFrom(
this.authService.AuthResetPassword({ username }, metadata),
);
}
async verifyResetPasswordCode({
@@ -107,19 +157,21 @@ export class AuthClientService implements OnModuleInit {
);
}
async confirmResetPassword({
username,
code,
newPassword,
}: AuthConfirmResetPasswordRequest) {
async confirmResetPassword(
{ username, code, newPassword }: AuthConfirmResetPasswordRequest,
metadata: Metadata,
) {
this.logger.info('confirmResetPassword');
return lastValueFrom(
this.authService.AuthConfirmResetPassword({
username,
code,
newPassword,
}),
this.authService.AuthConfirmResetPassword(
{
username,
code,
newPassword,
},
metadata,
),
);
}
@@ -152,4 +204,83 @@ export class AuthClientService implements OnModuleInit {
this.authService.AuthVerifyTotpMfa({ accessToken, totp }),
);
}
async getSession(session: string) {
return lastValueFrom(this.authService.AuthGetSession({ session }));
}
async oauthSignIn(data: { username: string; token: string }) {
const { username, token } = data;
return lastValueFrom(
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;
}
}
}
+131 -1
View File
@@ -1,4 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import {
AuthSignInRequest,
AuthSignInResponse,
} from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class LoginDto {
@ApiProperty()
@@ -10,3 +15,128 @@ export class LoginDto {
@ApiProperty({ required: false })
totp: string;
}
export class AuthTokens {
@ApiProperty()
accessToken: string;
@ApiProperty()
refreshToken: string;
@ApiPropertyOptional()
termsOfUseToken: string;
@ApiProperty({ deprecated: true })
idToken: string;
}
export enum MfaStatuses {
PENDING = 'pending',
NONE = 'none',
TOTP = 'totp',
}
export enum TouStatuses {
PENDING = 'pending',
REQUIRED = 'required',
OK = 'ok',
}
export class TermsOfUse {
@ApiProperty()
version: number;
@ApiProperty()
publicUrl: string;
@ApiProperty()
enforceDate: string;
@ApiProperty()
createdAt: string;
}
export class TermsOfUseStatus {
@ApiProperty({ enum: TouStatuses })
status: string;
@ApiPropertyOptional()
lastSigned?: TermsOfUse;
@ApiPropertyOptional()
next?: TermsOfUse;
}
export class AuthUser {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
username: string;
@ApiProperty()
createdAt: string;
}
export class AuthCustomer {
@ApiProperty()
modules: string[];
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
displayName: string;
@ApiProperty()
tier: string;
@ApiProperty()
scheduleLimit: string;
@ApiProperty()
links: Link[];
@ApiProperty()
themeEnabled: boolean;
@ApiProperty()
enforceMfa: boolean;
}
export class AuthSignInReq implements AuthSignInRequest {
@ApiProperty()
username: string;
@ApiProperty()
password: string;
@ApiPropertyOptional({
description:
'TOTP code used to login when Multi-Factor Authentication is enabled',
})
totp: string;
}
export class AuthSignInRes implements AuthSignInResponse {
@ApiProperty()
permissions: string[];
@ApiProperty({ enum: MfaStatuses })
mfaStatus: string;
@ApiPropertyOptional()
customer?: AuthCustomer;
@ApiPropertyOptional()
user?: AuthUser;
@ApiPropertyOptional()
tokens?: AuthTokens;
@ApiPropertyOptional()
termsOfUse?: TermsOfUseStatus;
}
export class AuthRefreshAccessTokenReq {
@ApiProperty()
refreshToken: string;
@ApiProperty()
userId: string;
}
export class AuthRefreshAccessTokenRes {
@ApiProperty()
permissions: string[];
@ApiProperty()
accessToken: string;
}
export interface BulkEditRequest {
users: string[];
}
export interface BulkEditResponse {
message: string;
successfulUsers: string[];
failedUsers: string[];
}
@@ -0,0 +1,46 @@
import {
AuthenticateOptionsGoogle,
Profile,
Strategy,
StrategyOptions,
} from 'passport-google-oauth20';
import { PassportStrategy } from '@nestjs/passport';
import { Inject, Injectable } from '@nestjs/common';
import { OauthSecrets } from 'src/utils/OauthSecrets';
import { Request } from 'express';
import jwt from 'jsonwebtoken';
@Injectable()
export class GoogleLoginStrategy extends PassportStrategy(
Strategy,
'google-login',
) {
redirect_uri: string;
constructor(
@Inject('OAUTH_SECRETS')
private readonly oauthSecrets: OauthSecrets,
) {
const options: StrategyOptions = {
clientID: oauthSecrets['google-login'].client_id,
clientSecret: oauthSecrets['google-login'].client_secret,
callbackURL: oauthSecrets['google-login'].redirect_uri,
scope: ['email', 'profile', 'openid'],
};
const verify = (
accessToken: string,
refreshToken: string,
profile: Profile,
done,
) => {
return done(null, profile, { accessToken, refreshToken });
};
super(options, verify);
}
authenticate(req: Request, options: AuthenticateOptionsGoogle) {
const language = req.headers['dadosfera-lang'] || req.query.language;
options.state = jwt.sign({ language }, process.env.JWT_PRIVATE_KEY);
super.authenticate(req, options);
}
}
+8 -4
View File
@@ -5,6 +5,11 @@ import {
} from '@nestjs/microservices';
import { credentials } from '@grpc/grpc-js';
import { Catalog } from '@dadosfera/protospack-v2';
import { PlatformInterfaces } from '@dadosfera/protospack-v2';
const isLocalConnection =
process.env.PIFACTORY_URL.startsWith('pi-factory:') ||
process.env.PIFACTORY_URL.includes('0.0.0.0');
export class CatalogClientConfiguration {
public name = 'CatalogClientConfiguration';
@@ -15,14 +20,13 @@ export class CatalogClientConfiguration {
package: [
Catalog.ProtoPackages.ReadPackage,
Catalog.ProtoPackages.WritePackage,
PlatformInterfaces.ProtoPackages.WritePackage
],
credentials:
process.env.LOCAL_ENV || process.env.ENV === 'local'
? undefined
: credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
Catalog.ProtoPaths.ReadFilePath,
Catalog.ProtoPaths.WriteFilePath,
PlatformInterfaces.ProtoPaths.WriteFilePath
],
loader: {
keepCase: true,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+330
View File
@@ -0,0 +1,330 @@
import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
import { CreateDataAssetRequest } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
export enum DataAssetShareType {
none = 'none',
public = 'public',
private = 'private',
}
export enum OrderEnum {
asc = 'asc',
desc = 'desc',
}
export class EmbedObject {
@ApiProperty()
url: string;
}
export class IRole {
@ApiProperty()
name: string;
@ApiProperty()
id: string;
}
export class IUser {
@ApiProperty()
username: string;
@ApiProperty()
id: string;
}
export class IComment {
@ApiProperty()
id: number;
@ApiProperty()
message: string;
@ApiProperty()
created_at: string;
@ApiProperty()
username: string;
}
export class IDataAsset {
@ApiProperty()
id: number;
@ApiPropertyOptional()
database_name?: string;
@ApiPropertyOptional()
table_name?: string;
@ApiPropertyOptional()
num_columns?: number;
@ApiPropertyOptional()
location?: string;
@ApiPropertyOptional()
created_time?: string;
@ApiPropertyOptional()
table_type?: string;
@ApiPropertyOptional()
size_in_bytes?: number;
@ApiPropertyOptional()
num_files?: number;
@ApiPropertyOptional()
data_encrypted?: string;
@ApiPropertyOptional()
num_rows?: number;
@ApiProperty()
display_name: string;
@ApiProperty()
pipeline_id: string;
@ApiProperty()
user_id: string;
@ApiProperty()
description: string;
@ApiProperty()
data_asset_type: string;
@ApiProperty()
tags: string[];
@ApiProperty()
owner: string;
@ApiProperty({ type: [IRole] })
roles: IRole[];
@ApiProperty({ type: [IUser] })
users: IUser[];
@ApiPropertyOptional()
dashboard_id?: number;
@ApiPropertyOptional()
name?: string;
@ApiPropertyOptional()
parameters?: string[];
@ApiPropertyOptional()
embedding_params?: object;
@ApiProperty()
created_at: string;
@ApiPropertyOptional()
updated_at?: string;
@ApiProperty({ type: [IComment] })
comments: IComment[];
@ApiPropertyOptional()
embed?: EmbedObject;
@ApiPropertyOptional({ enum: DataAssetShareType })
share_type?: DataAssetShareType;
}
export class IOneDataAsset {
@ApiProperty()
data_asset: IDataAsset;
}
export class IMakeAComment {
@ApiProperty()
message: string;
}
export class CommentToDelete extends PickType(IComment, ['id', 'username']) {}
export class IDeleteComment {
@ApiProperty()
comment: CommentToDelete;
}
export class ICatalogAllRequest {
@ApiPropertyOptional({
description: 'Texto usado para filtrar os ativos no catálogo.',
})
search;
@ApiPropertyOptional({
description: 'Número de registros a ser retornado',
maximum: 10000,
minimum: 1,
})
size: number;
@ApiPropertyOptional({
description: 'Usado para paginação, em conjunto com `size`',
minimum: 1,
})
page: number;
@ApiPropertyOptional({
description:
'Campo do data asset para usar na ordenação. Padrão: `created_at` ',
})
sort_by: string;
@ApiPropertyOptional({
enum: OrderEnum,
default: OrderEnum.asc,
description: 'Tipo de ordenação - `asc`: crescente; `desc`: decrescente ',
})
order?: OrderEnum;
}
export class ICatalogAllResponse {
@ApiProperty({ type: [IDataAsset] })
data_assets: IDataAsset[];
@ApiProperty()
total: number;
}
export class IData {
@ApiProperty()
var: number;
@ApiProperty()
date: string;
@ApiProperty()
last: number;
@ApiProperty()
maximum: number;
@ApiProperty()
minimum: number;
@ApiProperty()
day_opening: number;
}
export class IUpdateDataRequest {
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiProperty()
tags: string[];
@ApiPropertyOptional()
embed: EmbedObject;
@ApiPropertyOptional({ enum: DataAssetShareType })
share_type?: DataAssetShareType;
}
export class ICreateDataAsset implements CreateDataAssetRequest {
@ApiProperty()
display_name: string;
@ApiProperty()
description: string;
@ApiProperty()
data_asset_type: string;
@ApiProperty()
external_url: string;
@ApiProperty()
location: string;
@ApiPropertyOptional()
embed: EmbedObject;
}
export class IPreview {
@ApiProperty()
id: string;
@ApiProperty()
database_name: string;
@ApiProperty()
table_name: string;
@ApiProperty({ type: [IData] })
data: IData[];
}
export class IDocs {
@ApiProperty()
id: string;
@ApiProperty()
docs: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
table_id: string;
}
export class IColumnMetadata {
@ApiProperty()
id: string;
@ApiProperty()
table_name: string;
@ApiProperty()
column_name: string;
@ApiProperty()
data_type: string;
@ApiProperty()
missing_count: string;
@ApiProperty()
min: string;
@ApiProperty()
max: string;
@ApiProperty()
count: string;
@ApiProperty()
histogram: string;
@ApiProperty()
mean: string;
@ApiProperty()
median: string;
@ApiProperty()
missing_rate: string;
@ApiProperty()
stddev: string;
@ApiProperty()
unique_count: string;
@ApiProperty()
unique_rate: string;
@ApiProperty()
analyzer_rules: string[];
@ApiProperty()
version: string;
}
export class IPreviewResponse {
@ApiProperty()
preview: IPreview;
}
export class IDocsResponse {
@ApiProperty()
docs: IDocs;
}
export class IColumnsMetadataResponse {
@ApiProperty({ type: [IColumnMetadata] })
columns_metadata: IColumnMetadata[];
}
export class TriggerCatalogReq {
@ApiProperty({
description: 'Name of the table on Snowflake',
example: 'TB__4DXSD4_TEST',
})
table_name: string;
@ApiPropertyOptional({
description:
'Schema where the asset is located. If not set defaults to "PUBLIC"',
})
table_schema?: string;
@ApiPropertyOptional({
description: 'Id of the pipeline that generated the asset.',
})
pipeline_id: string;
}
export class TriggerCatalogRes {
@ApiProperty()
session: string;
}
export class GetDatasetCatalogTaskRes {
@ApiProperty()
created_at: string;
@ApiProperty()
customer_name: string;
@ApiProperty()
session_id: string;
@ApiProperty()
status: string;
@ApiProperty()
table_name: string;
@ApiProperty()
updated_at: string;
@ApiProperty()
updated_by: string;
}
export class BatchRemoveRlsRulesRequest {
@ApiPropertyOptional()
nimbus_dashboard_id?: string;
@ApiPropertyOptional()
id_rls?: string;
}
export type AssetReporter = {
id: string;
display_name: string;
data_asset_type: string;
created_at: string;
tags: string;
}
+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;
}
@@ -6,14 +6,17 @@ import {
} from '@nestjs/microservices';
import { ConnectionTest } from '@dadosfera/protospack-v2';
const isLocalConnection =
process.env.INFACTORY_URL.startsWith('in-factory:') ||
process.env.INFACTORY_URL.includes('0.0.0.0');
export class ConnectionTestClientConfiguration {
private config: GrpcOptions = {
transport: Transport.GRPC,
options: {
url: process.env.INFACTORY_URL,
package: [ConnectionTest.ProtoPackages.ReadPackage],
credentials:
process.env.ENV === 'local' ? undefined : credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ConnectionTest.ProtoPaths.ReadFilePath],
loader: {
keepCase: true,
@@ -9,7 +9,7 @@ import {
ValidationPipe,
} from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { User, RequestUser } from 'src/authentication/user.decorator';
import { User, RequestUser } from 'src/decorators/user.decorator';
import { ConnectionTestService } from './connection-test.service';
import {
ConnectionTestPingRes,
@@ -24,9 +24,11 @@ import {
GetTableMetadataReq,
} from './dto/connection-test';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { Authenticated } from 'src/authentication/authentication.decorator';
import { Authenticated } from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('Connection Test')
@Controller('connection-test')
@UseFilters(new GrpcToHttpExceptionFilter())
@@ -19,7 +19,7 @@ import {
CreateConnectionDto,
DatabaseConnectionPropertiesDto,
} from '../connection/dtos/connection';
import { RequestUser } from 'src/authentication/user.decorator';
import { RequestUser } from 'src/decorators/user.decorator';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
@Injectable()
+5 -4
View File
@@ -6,6 +6,10 @@ import {
} from '@nestjs/microservices';
import { ConnectionManager } from '@dadosfera/protospack-v2';
const isLocalConnection =
process.env.INFACTORY_URL.startsWith('in-factory:') ||
process.env.INFACTORY_URL.includes('0.0.0.0');
export class ConnectionClientConfiguration {
public name = 'ConnectionClientConfiguration';
private config: GrpcOptions = {
@@ -16,10 +20,7 @@ export class ConnectionClientConfiguration {
ConnectionManager.ProtoPackages.WritePackage,
ConnectionManager.ProtoPackages.ReadPackage,
],
credentials:
process.env.LOCAL_ENV || process.env.ENV === 'local'
? undefined
: credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
ConnectionManager.ProtoPaths.WriteFilePath,
ConnectionManager.ProtoPaths.ReadFilePath,
+6 -36
View File
@@ -1,9 +1,4 @@
import {
OnModuleInit,
Inject,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { OnModuleInit, Inject } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { ConnectionManager } from '@dadosfera/protospack-v2';
import { lastValueFrom } from 'rxjs';
@@ -86,12 +81,7 @@ export class ConnectionClientService implements OnModuleInit {
},
metadata,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async updateConnection(
@@ -112,12 +102,7 @@ export class ConnectionClientService implements OnModuleInit {
},
metadata,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async deleteConnection({ body, metadata }): Promise<ConnectionRes> {
@@ -130,12 +115,7 @@ export class ConnectionClientService implements OnModuleInit {
},
metadata,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async getAllConnections({ body, metadata }): Promise<ConnectionsRes> {
@@ -146,12 +126,7 @@ export class ConnectionClientService implements OnModuleInit {
{ search, filters: JSON.stringify(filters), size, page },
metadata,
),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async getConnectionDetails({
@@ -162,12 +137,7 @@ export class ConnectionClientService implements OnModuleInit {
return lastValueFrom(
this.connectionServiceRead.GetConnectionDetails({ id }, metadata),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async INTERNAL_getAllConnections({
+23 -14
View File
@@ -7,31 +7,36 @@ import {
Param,
Delete,
Get,
Headers,
Query,
UseFilters,
} from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger';
import { ConnectionClientService } from './client.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { ValidationPipe } from '../../pipes/object-validation.pipe';
import {
ConnectionDetailsRes,
ConnectionRes,
ConnectionsRes,
GetAllConnectionsReq,
UpdateConnectionDto,
} from './dtos/connection';
import { CreateConnectionDto } from './dtos/connection';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
const connectionPermissions = PERMISSIONS_GROUPS.CONNECTION.permissions;
@UseFilters(new GrpcToHttpExceptionFilter())
@ApiTags('connections')
@ApiBearerAuth()
@Authenticated()
@Controller('connections')
export class ConnectionController {
@@ -44,7 +49,9 @@ export class ConnectionController {
this.logger = dadosferaLogger.logger;
}
@ApiInternalOnlyEndpoint()
@Get('connections-migration')
@ApiExcludeEndpoint()
@RequireAllPermissions(
PERMISSIONS_GROUPS.DADOSFERA.permissions.CONNECTIONS_MIGRATION,
)
@@ -80,12 +87,11 @@ export class ConnectionController {
@RequireAllPermissions(connectionPermissions.CREATE)
async updateConnection(
@User() user: RequestUser,
@Headers('Dadosfera-Lang') language: string,
@Language() language: LanguageEnum,
@Param('id') id: string,
@Body(new ValidationPipe()) body: UpdateConnectionDto,
): Promise<ConnectionRes> {
this.logger.info('/connections - Update Connection');
if (!language) language = 'en-us';
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
@@ -132,11 +138,10 @@ export class ConnectionController {
@Get()
async getAllConnections(
@User() user: RequestUser,
@Headers('Dadosfera-Lang') language,
@Query() queries,
@Language() language: LanguageEnum,
@Query() queries: GetAllConnectionsReq,
): Promise<ConnectionsRes> {
this.logger.info('/connections - Get All Connection');
if (!language) language = 'en-us';
const { user_id, customer_id, customer_name, username } = user;
const { search, size, page, ...filters } = queries;
@@ -158,13 +163,16 @@ export class ConnectionController {
@Get('/:id')
async getConnectionDetails(
@Headers('Dadosfera-Lang') language,
@Language() language: LanguageEnum,
@User() user: RequestUser,
@Param('id') id,
@Query('details') details,
@Param('id') id: string,
@Query() queries,
): Promise<ConnectionDetailsRes> {
this.logger.info('/connections - Get Connection Details');
if (!language) language = 'en-us';
console.log(queries);
const { details, sensitive } = queries;
const { user_id, customer_id, customer_name, username } = user;
const metadata = PackTheMetadata({
@@ -174,6 +182,7 @@ export class ConnectionController {
username,
details,
language,
sensitive,
});
const response: any = await this.clientService.getConnectionDetails({
+14 -2
View File
@@ -9,6 +9,7 @@ import {
ConnectionToCatalog,
} from '@dadosfera/protospack-v2/dist/lib/ConnectionManager/interfaces/entities';
import {
GetAllConnectionRequest,
GetAllConnectionResponse,
GetConnectionDetailsResponse,
GetConnectionResponse,
@@ -21,7 +22,7 @@ export type ConnectionCredentialsType =
| 'oauth'
| 'api_key'
| 'service_account'
| 'headers_authF';
| 'headers_auth';
export interface ConnectionApiConnection {
config_id: string;
plugin: string;
@@ -94,7 +95,6 @@ export class ConnectionDto implements Connection {
export class ConnectionToCatalogDto implements ConnectionToCatalog {
// ---Automatically generated information - will not be sent by the frontend--- //
id: string;
customer_id: string;
updated_at: string;
created_at: string;
@@ -104,6 +104,9 @@ export class ConnectionToCatalogDto implements ConnectionToCatalog {
customer_name: string;
// ---Information sent by the frontend--- //
@ApiPropertyOptional()
id: string;
@ApiProperty()
name: string;
@@ -147,6 +150,15 @@ export class UpdateConnectionDto extends PickType(ConnectionDto, [
'properties',
]) {}
export class GetAllConnectionsReq implements GetAllConnectionRequest {
@ApiPropertyOptional()
search?: string;
@ApiPropertyOptional()
page?: string;
@ApiPropertyOptional()
size?: string;
}
export class ConnectionsRes implements GetAllConnectionResponse {
@ApiProperty({ type: [ConnectionToCatalogDto] })
connections: ConnectionToCatalogDto[];
+5 -4
View File
@@ -6,6 +6,10 @@ import {
} from '@nestjs/microservices';
import { ConnectorManager } from '@dadosfera/protospack-v2';
const isLocalConnection =
process.env.INFACTORY_URL.startsWith('in-factory:') ||
process.env.INFACTORY_URL.includes('0.0.0.0');
export class ConnectorClientConfiguration {
public name = 'ConnectorClientConfiguration';
private config: GrpcOptions = {
@@ -16,10 +20,7 @@ export class ConnectorClientConfiguration {
ConnectorManager.ProtoPackages.WritePackage,
ConnectorManager.ProtoPackages.ReadPackage,
],
credentials:
process.env.LOCAL_ENV || process.env.ENV === 'local'
? undefined
: credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
ConnectorManager.ProtoPaths.WriteFilePath,
ConnectorManager.ProtoPaths.ReadFilePath,
+24 -160
View File
@@ -83,161 +83,30 @@ export class ConnectorClientService implements OnModuleInit {
);
});
const responseParsed = JSON.parse(res.message);
const connectorErrors = responseParsed.items
.filter((i) => i.update._index === 'connector' && i.update.error)
const connectorsResponse = responseParsed.items.filter(
(i) => i.update._index === process.env.CONNECTORS_INDEX,
);
const connectorErrors = connectorsResponse
.filter((i) => i.update.error)
.map((i) => ({
id: i.update._id,
reason: i.update.error,
status: i.error.status,
}));
const connectionControlsErrors = responseParsed.items
.filter(
(i) => i.update._index === 'connection_controls' && i.update.error,
)
.map((i) => ({
id: i.update._id,
reason: i.update.error,
status: i.error.status,
}));
const connectionStepsErrors = responseParsed.items
.filter((i) => i.update._index === 'connection_steps' && i.update.error)
.map((i) => ({
id: i.update._id,
reason: i.update.error,
status: i.error.status,
}));
const configControlsErrors = responseParsed.items
.filter((i) => i.update._index === 'config_controls' && i.update.error)
.map((i) => ({
id: i.update._id,
reason: i.update.error,
status: i.error.status,
}));
const connectorCreated = responseParsed.items
.filter(
(i) => i.update._index === 'connector' && i.update.result === 'created',
)
.map((i) => ({
id: i.update._id,
}));
const connectionControlsCreated = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_controls' &&
i.update.result === 'created',
)
.map((i) => ({
id: i.update._id,
}));
const connectionStepsCreated = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_steps' &&
i.update.result === 'created',
)
.map((i) => ({
id: i.update._id,
}));
const configControlsCreated = responseParsed.items
.filter(
(i) =>
i.update._index === 'config_controls' &&
i.update.result === 'created',
)
.map((i) => ({
id: i.update._id,
}));
const connectorUpdated = responseParsed.items
.filter(
(i) => i.update._index === 'connector' && i.update.result === 'updated',
)
.map((i) => ({
id: i.update._id,
}));
const connectionControlsUpdated = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_controls' &&
i.update.result === 'updated',
)
.map((i) => ({
id: i.update._id,
}));
const connectionStepsUpdated = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_steps' &&
i.update.result === 'updated',
)
.map((i) => ({
id: i.update._id,
}));
const configControlsUpdated = responseParsed.items
.filter(
(i) =>
i.update._index === 'config_controls' &&
i.update.result === 'updated',
)
.map((i) => ({
id: i.update._id,
}));
const connectorNoop = responseParsed.items
.filter(
(i) => i.update._index === 'connector' && i.update.result === 'noop',
)
.map((i) => ({
id: i.update._id,
}));
const connectionControlsNoop = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_controls' &&
i.update.result === 'noop',
)
.map((i) => ({
id: i.update._id,
}));
const connectionStepsNoop = responseParsed.items
.filter(
(i) =>
i.update._index === 'connection_steps' && i.update.result === 'noop',
)
.map((i) => ({
id: i.update._id,
}));
const configControlsNoop = responseParsed.items
.filter(
(i) =>
i.update._index === 'config_controls' && i.update.result === 'noop',
)
.map((i) => ({
id: i.update._id,
id: i.update?._id,
reason: i.update?.error,
status: i.error?.status,
}));
const connectorCreated = connectorsResponse
.filter((i) => i.update.result === 'created')
.map((i) => i.update._id);
const connectorUpdated = connectorsResponse
.filter((i) => i.update.result === 'updated')
.map((i) => i.update._id);
const connectorNoop = connectorsResponse
.filter((i) => i.update.result === 'noop')
.map((i) => i.update._id);
return {
errors: {
connectors: connectorErrors,
connection_controls: connectionControlsErrors,
connection_steps: connectionStepsErrors,
config_controls: configControlsErrors,
},
created: {
connectors: connectorCreated,
connection_controls: connectionControlsCreated,
connection_steps: connectionStepsCreated,
config_controls: configControlsCreated,
},
updated: {
connectors: connectorUpdated,
connection_controls: connectionControlsUpdated,
connection_steps: connectionStepsUpdated,
config_controls: configControlsUpdated,
},
noop: {
connectors: connectorNoop,
connection_controls: connectionControlsNoop,
connection_steps: connectionStepsNoop,
config_controls: configControlsNoop,
},
errors: connectorErrors,
created: connectorCreated,
updated: connectorUpdated,
noop: connectorNoop,
};
}
@@ -263,13 +132,13 @@ export class ConnectorClientService implements OnModuleInit {
});
}
async getConnectorDetails(plugin: string, language: string) {
async getConnectorDetails(connectorId: string, language: string) {
const meta = new Metadata();
meta.add('Language', language);
return lastValueFrom(
this.connectorServiceRead.GetConnectorDetails(
{
plugin,
plugin: connectorId,
},
meta,
),
@@ -316,12 +185,7 @@ export class ConnectorClientService implements OnModuleInit {
version,
plugin,
}),
).catch((err) => {
throw new HttpException(
err.details,
err.code === 6 ? HttpStatus.CONFLICT : 400,
);
});
);
}
async updateConnector({ plugin, changes }) {
+13 -19
View File
@@ -11,15 +11,13 @@ import {
UploadedFile,
UseInterceptors,
Inject,
Headers,
UploadedFiles,
} from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger';
import { ApiConsumes, ApiTags } from '@nestjs/swagger';
import { ConnectorClientService } from './client.service';
import { AddTagDto } from './dtos/add-tag';
import { CreateConnectorDto } from './dtos/create-connector';
import { DeleteConnectorDto } from './dtos/delete-connector';
import { GetAllDto } from './dtos/get-all';
import { RemoveTagDto } from './dtos/remove-tag';
import { UpdateDto } from './dtos/update';
@@ -28,11 +26,14 @@ import {
Authenticated,
RequireAllPermissions,
RequireSomePermission,
} from 'src/authentication/authentication.decorator';
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('connectors')
@ApiBearerAuth()
@Authenticated()
@Controller('connectors')
export class ConnectorController {
@@ -99,11 +100,10 @@ export class ConnectorController {
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getAllConnectors(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Query() queries: GetAllDto,
) {
this.logger.info('/GET - getAllConnectors Route');
if (!language) language = 'en-us';
const { search, size, page, ...filters } = queries;
@@ -144,12 +144,11 @@ export class ConnectorController {
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getConnector(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
this.logger.info('/GET/:plugin - getConnector Route');
if (!language) language = 'en-us';
const pluginId = `${plugin}-${version}`;
@@ -173,13 +172,11 @@ export class ConnectorController {
PERMISSIONS_GROUPS.PIPELINE.permissions.DELETE,
)
async getConnectorDetails(
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
@Param('plugin') plugin: string,
@Query('version') version: string,
) {
this.logger.info('/upload - Upload Connector Route');
if (!language) language = 'en-us';
const pluginId = `${plugin}-${version}`;
const response: any = await this.connectorClientService.getConnectorDetails(
@@ -274,18 +271,15 @@ export class ConnectorController {
@RequireAllPermissions(PERMISSIONS_GROUPS.CONNECTORS.permissions.DELETE)
async deleteConnector(
@Param('plugin') plugin: string,
@Body() { version }: DeleteConnectorDto,
@Query('version') version: string,
) {
this.logger.info('/upload - Upload Connector Route');
this.logger.info('DELETE /:plugin - Delete Connector Route');
const response: any = await this.connectorClientService.deleteConnector(
const response = await this.connectorClientService.deleteConnector(
plugin,
version,
);
return {
message: response.message || 'ok',
connector: { ...JSON.parse(response.connector) },
};
return { message: response.message, connector: response.connector };
}
}
@@ -0,0 +1,139 @@
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
Inject,
Param,
Post,
Put,
Query,
UseFilters,
} from '@nestjs/common';
import { ApiOkResponse, ApiProduces, ApiTags } from '@nestjs/swagger';
import { DADOSFERA_MODULES_KEYS, PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
RequireModule,
RequireSomePermission,
} from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomersService } from './customers.service';
import { CustomerLinkRequest, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { EnforceMfa } from './dtos/enforce-mfa';
@ApiTags('Customers')
@Controller('customers')
@UseFilters(GrpcToHttpExceptionFilter)
export class CustomersController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private customersService: CustomersService,
) {
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')
@Authenticated()
@ApiOkResponse({ type: CustomerLinksResponse })
async getCustomerLinks(@Param('id') id: string) {
this.logger.info('getCustomerLinks', { id });
const links = await this.customersService.getLinks(id);
return { links };
}
@Put(':id/links')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse()
@HttpCode(HttpStatus.OK)
async setCustomerLinks(
@Body() body: CustomerLinkRequest,
@Param('id') id: string,
) {
const { links } = body;
this.logger.info('setCustomerLinks', { id, links });
await this.customersService.setLinks(id, links);
}
@Get('token')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.AUTH.permissions.GENERATE_TOKEN)
@ApiProduces('text/plain')
async getCustomerToken(
@Query('exp') exp: StringValue,
@User() user: RequestUser,
): Promise<string> {
this.logger.info('getCustomerToken', { exp, user });
const data = {
customerId: user.customer_id,
userId: user.user_id,
customerName: user.customer_name,
};
return this.customersService.generateToken(exp, data);
}
@Get('monitoring-dashboard')
@Authenticated()
@RequireAllPermissions(
PERMISSIONS_GROUPS.CUSTOMER.permissions.MONITORING_DASHBOARD,
)
async getCustomerMonitoringDashboard(
@User() user: RequestUser,
): Promise<{ url: string }> {
this.logger.info('getCustomerMonitoringDashboard');
const metadata = PackTheMetadata(user);
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;
}
}
+23
View File
@@ -0,0 +1,23 @@
import { Module } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ClientsModule } from '@nestjs/microservices';
import { DucClient } from '../duc/client.config';
import { CustomersController } from './customers.controller';
import { CustomersService } from './customers.service';
import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client';
const ducClient = new DucClient();
const piFactoryClient = new PipelinesClientConfiguration();
@Module({
imports: [
ClientsModule.register([
ducClient.providerOptions,
piFactoryClient.providerOptions,
]),
],
controllers: [CustomersController],
providers: [CustomersService, DadosferaLogger],
exports: [CustomersService],
})
export class CustomersModule {}
+216
View File
@@ -0,0 +1,216 @@
import {
OnModuleInit,
Inject,
Injectable,
HttpException,
HttpStatus,
InternalServerErrorException,
ForbiddenException,
} from '@nestjs/common';
import { firstValueFrom, lastValueFrom } from 'rxjs';
import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import { DucClient } from '../duc/client.config';
import { ClientGrpc } from '@nestjs/microservices';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { CustomerUpdateRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { CustomersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import ErrorCodes from 'src/utils/errorCodes';
import jwt from 'jsonwebtoken';
import {
GetSecretValueCommand,
SecretsManagerClient,
} from '@aws-sdk/client-secrets-manager';
import getEnv from 'src/utils/getEnv';
import { logger } from 'elastic-apm-node';
import {
ReadService,
ProtoServices as PipelineProtoServices,
} from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
import { Metadata } from '@grpc/grpc-js';
import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
// This function will accept any string, which may result in a bug.
@Injectable()
export class CustomersService implements OnModuleInit {
private customerService: CustomersProtoService;
private logger: DadosferaLogger;
private pipelineReadService: ReadService.PipelineV2ReadService;
constructor(
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
@Inject(PipelinesClientConfiguration.name)
private readonly pipelinesGrpcClient: ClientGrpc,
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.customerService = this.grpcClient.getService<CustomersProtoService>(
ProtoServices.CustomersProtoService,
);
this.pipelineReadService =
this.pipelinesGrpcClient.getService<ReadService.PipelineV2ReadService>(
PipelineProtoServices.PipelineV2ReadService,
);
}
async getLinks(customerId: string) {
try {
const result = await lastValueFrom(
this.customerService.CustomerFindOneById({ id: customerId }),
);
return result.customer?.links || [];
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND)
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
throw err;
}
}
async setLinks(customerId: string, links: Link[]) {
if (!customerId || !links) {
throw new HttpException(null, HttpStatus.BAD_REQUEST);
}
try {
return await firstValueFrom(
this.customerService.CustomerUpdate({
id: customerId,
links,
} as CustomerUpdateRequest),
);
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND)
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
else throw err;
}
}
async generateToken(
expiresIn = '30m',
data: { customerId: string; userId: string; customerName: string },
) {
const { customerId, userId: generatedById, customerName } = data;
const secretsManagerClient = new SecretsManagerClient({});
const getSecretComand = new GetSecretValueCommand({
SecretId: `${getEnv()}/${customerName}/jwt-signing-key`,
});
let private_key_pem;
await secretsManagerClient
.send(getSecretComand)
.then((res) => {
const secret = JSON.parse(res.SecretString);
private_key_pem = secret.private_key_pem;
})
.catch((err) => {
logger.error(err.stack);
throw new InternalServerErrorException(
'Error finding keys for customer',
);
});
let jwt_token;
try {
jwt_token = jwt.sign(
{
customerId,
generatedById,
},
private_key_pem,
{
algorithm: 'RS256',
expiresIn,
issuer: 'maestro',
},
);
} catch (err) {
logger.error(err.stack);
throw new InternalServerErrorException('Error generating token');
}
// const verify = jwt.verify(jwt_token, public_key_pem, {
// algorithms: ['RS256'],
// issuer: 'maestro',
// });
// const decoded = jwt.decode(jwt_token, { complete: true });
return jwt_token;
}
async getMonitoringDashboardUrl(metadata: Metadata) {
logger.info('CustomersService - getMonitoringDashboardUrl');
const res = await lastValueFrom(
this.pipelineReadService.PipelineV2GetDashboardUrl(
{
dashboard_id: '98',
exp: '15m',
metabase_customer_name: 'dadosferatech',
},
metadata,
),
);
logger.info('Done');
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
})
)
}
}
+23
View File
@@ -0,0 +1,23 @@
import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CustomerLink implements Link {
@ApiProperty()
href: string;
@ApiProperty()
name: string;
@ApiProperty()
description: string;
@ApiPropertyOptional()
iconSrc: string;
}
export class CustomerLinkRequest {
@ApiProperty({ type: [CustomerLink] })
links: CustomerLink[];
}
export class CustomerLinksResponse {
@ApiProperty({ type: [CustomerLink] })
links: CustomerLink[];
}
@@ -0,0 +1,6 @@
import { ApiProperty } from "@nestjs/swagger";
export class EnforceMfa {
@ApiProperty()
enabled: boolean
}
+9 -6
View File
@@ -9,6 +9,10 @@ import {
ProtoPaths,
} from '@dadosfera/protospack-v2/dist/lib/Duc';
const isLocalConnection =
process.env.DUC_URL.startsWith('duc:') ||
process.env.DUC_URL.includes('0.0.0.0');
export class DucClient {
public name = 'DucClient';
@@ -16,18 +20,17 @@ export class DucClient {
transport: Transport.GRPC,
options: {
url: process.env.DUC_URL,
package: ProtoPackages.WritePackage,
credentials:
process.env.LOCAL_ENV || process.env.ENV === 'local'
? undefined
: credentials.createSsl(),
protoPath: ProtoPaths.WriteFilePath,
package: [ProtoPackages.WritePackage, ProtoPackages.ReadPackage],
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [ProtoPaths.WriteFilePath, ProtoPaths.ReadFilePath],
loader: {
keepCase: true,
enums: String,
objects: true,
arrays: true,
},
maxSendMessageLength: 15 * 1024 * 1024, // 15 MB por mensagem
maxReceiveMessageLength: 15 * 1024 * 1024,
},
};
@@ -0,0 +1,47 @@
import { ApiProperty } from "@nestjs/swagger";
export class CreateIdentityProvider {
@ApiProperty()
name: string;
@ApiProperty()
clientId: string;
@ApiProperty()
clientSecret: string;
@ApiProperty()
issuerUrl: string;
@ApiProperty()
permissions: number[];
}
export class IdentityProviderResponse {
@ApiProperty()
id: string;
@ApiProperty()
name: string;
@ApiProperty()
clientId: string;
@ApiProperty()
issueUrl: string;
@ApiProperty()
permissions: {
id: number;
name: string;
}[];
}
export class IdentityProviderListResponse {
@ApiProperty()
providers: IdentityProviderResponse[]
}
@@ -0,0 +1,10 @@
export class SsoSignInDto {
readonly nonce: string;
readonly codeVerifier: string;
readonly state: string;
readonly id: string;
readonly clientId: string;
readonly clientSecret: string;
readonly issuerUrl: string;
readonly redirectUrls: string[];
}
@@ -0,0 +1,206 @@
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Inject,
Param,
Post,
Put,
Redirect,
Req,
} from '@nestjs/common';
import { IdentityProviderService } from './identity-provider.service';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { ApiOkResponse } from '@nestjs/swagger';
import {
CreateIdentityProvider,
IdentityProviderListResponse,
IdentityProviderResponse,
} from './dto/identity-provider.dto';
import { Request } from 'express';
import ErrorCodes from 'src/utils/errorCodes';
import {
Authenticated,
RequireModule,
RequireSomePermission,
} from 'src/decorators/authentication.decorator';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
@Controller('identity-providers')
export class IdentityProviderController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private identityProviderService: IdentityProviderService,
) {
this.logger = dadosferaLogger.logger;
}
@Post()
@HttpCode(HttpStatus.OK)
@ApiOkResponse({ type: IdentityProviderResponse })
@Authenticated()
@RequireModule('sso')
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async addIdentityProvider(
@User() user: RequestUser,
@Body() body: CreateIdentityProvider,
@Language() language: LanguageEnum,
) {
this.logger.info('POST /identity-providers');
const metadata = PackTheMetadata({
...user,
language,
});
return await this.identityProviderService.create(body, metadata);
}
@Get()
@HttpCode(HttpStatus.OK)
@ApiOkResponse({ type: IdentityProviderListResponse })
@Authenticated()
@RequireModule('sso')
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async getProviders(
@User() user: RequestUser,
@Language() language: LanguageEnum,
) {
this.logger.info('GET identity-providers');
const metadata = PackTheMetadata({
...user,
language,
});
const result = await this.identityProviderService.getList(metadata);
return result;
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
@RequireModule('sso')
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async deleteIdentityProvider(
@Param('id') id: string,
@User() user: RequestUser,
) {
this.logger.info('DELETE /identity-providers');
const metadata = PackTheMetadata({
...user,
});
return await this.identityProviderService.deleteIdentityProvider(
id,
metadata,
);
}
@Put(':id')
@HttpCode(HttpStatus.OK)
@Authenticated()
@RequireModule('sso')
@RequireSomePermission(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
async updateIdentityProviders(
@Param('id') id: string,
@Body() body: CreateIdentityProvider,
@User() user: RequestUser,
) {
this.logger.info('PUT /identity-providers');
const metadata = PackTheMetadata({
...user,
});
return await this.identityProviderService.updateIdentityProviders(
id,
body,
metadata,
);
}
@Post('/callback')
@HttpCode(HttpStatus.OK)
async callbackIdp(
@Req() req: Request,
@Language() language: LanguageEnum,
@Body()
body: {
state: string;
code: string;
},
) {
this.logger.info('GET /identity-providers/callback');
const { code, state } = body;
if (!code) {
this.logger.error('No code received from IDP');
throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE);
}
if (!state) {
this.logger.error('No state received from IDP');
throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE);
}
const origin = req.headers['origin'] as string;
const lang = language.substring(0, 2) + language.substring(2).toUpperCase();
const callbackUrl = process.env.ENV !== "prd" ? `${origin}/auth/callback` : `${origin}/${lang}/auth/callback`;
return await this.identityProviderService.getTokenByIdp(code, state, callbackUrl);
}
@Get('/links')
@HttpCode(HttpStatus.OK)
async providerLinks(@Req() req: Request) {
this.logger.info('GET /identity-providers/links');
const frontDomain = req.headers['origin'] as string;
if (!frontDomain) {
throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER);
}
const result =
await this.identityProviderService.identityProvidersLinksPerDomain(
frontDomain,
);
return result;
}
@Get(':id')
@HttpCode(HttpStatus.OK)
@Redirect()
async loginIdp(@Param('id') id: string, @Req() req: Request, @Language() language: LanguageEnum) {
this.logger.info('GET /identity-providers/:id');
const frontDomain = req.headers['origin'] as string || req.headers['referer'] as string;
this.logger.info(`Front domain: ${frontDomain}`);
const host = frontDomain.lastIndexOf('/') !== -1
? frontDomain.substring(0, frontDomain.lastIndexOf('/'))
: frontDomain;
const lang = language.substring(0, 2) + language.substring(2).toUpperCase();
const callbackUrl = process.env.ENV !== "prd" ? `${host}/auth/callback` : `${host}/${lang}/auth/callback`;
const redirectUrl =
await this.identityProviderService.loginIdentityProvider(id, callbackUrl);
this.logger.info(`Redirecting to: ${redirectUrl}`);
return {
url: redirectUrl,
};
}
}
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { IdentityProviderController } from './identity-provider.controller';
import { IdentityProviderService } from './identity-provider.service';
import { ClientsModule } from '@nestjs/microservices';
import { DucClient } from '../duc/client.config';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { ServicesModule } from 'src/services/service.module';
const client = new DucClient();
@Module({
imports: [ClientsModule.register([client.providerOptions]), ServicesModule],
controllers: [IdentityProviderController],
providers: [IdentityProviderService, DadosferaLogger],
})
export class IdentityProviderModule {}
@@ -0,0 +1,163 @@
import {
BadRequestException,
Inject,
Injectable,
OnModuleInit,
} from '@nestjs/common';
import { DucClient } from '../duc/client.config';
import { ClientGrpc } from '@nestjs/microservices';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { IdentityProviderProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { lastValueFrom } from 'rxjs';
import { IdentityProviderRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { Metadata } from '@grpc/grpc-js';
import { Issuer, generators } from 'openid-client';
import { SsoSignInDto } from './dto/sso-signin.dto';
import { CacheService } from 'src/services/cache.service';
import { Request } from 'express';
import { CreateIdentityProvider } from './dto/identity-provider.dto';
@Injectable()
export class IdentityProviderService implements OnModuleInit {
private identityProviderService: IdentityProviderProtoService;
constructor(
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
private readonly cacheService: CacheService<SsoSignInDto>,
) {}
onModuleInit() {
this.identityProviderService =
this.grpcClient.getService<IdentityProviderProtoService>(
ProtoServices.IdentityProviderProtoService,
);
}
async create(body: IdentityProviderRequest, metadata: Metadata) {
return await lastValueFrom(
this.identityProviderService.Create(body, metadata),
);
}
async getList(metadata: Metadata) {
return await lastValueFrom(
this.identityProviderService.GetList({}, metadata),
);
}
async loginIdentityProvider(id: string, callbackUrl: string) {
const idp = await lastValueFrom(
this.identityProviderService.FindIdentityProvider({ id }),
);
const issuer = await Issuer.discover(idp.issuerUrl);
const client = new issuer.Client({
client_id: idp.clientId,
client_secret: idp.clientSecret,
redirect_uris: idp.redirectUrls,
response_types: ['code'],
});
const code_verifier: string = generators.codeVerifier();
const code_challenge: string = generators.codeChallenge(code_verifier);
const state = generators.state();
const nonce = generators.nonce();
// Using state because it is returned in the callback
// and we can use it to retrieve the code_verifier and nonce
await this.cacheService.set(state, {
codeVerifier: code_verifier,
nonce,
id: idp.id,
state,
clientId: idp.clientId,
clientSecret: idp.clientSecret,
issuerUrl: idp.issuerUrl,
redirectUrls: idp.redirectUrls,
});
const url = client.authorizationUrl({
scope: 'openid email',
response_type: 'code',
code_challenge,
code_challenge_method: 'S256',
state,
nonce,
redirect_uri: callbackUrl,
});
const idpUrl = url + '&identity_provider=' + idp.name;
return idpUrl;
}
async getTokenByIdp(code: string, state: string, callbackUrl: string) {
const ssoSign = await this.cacheService.get(state);
if (!ssoSign) {
throw new BadRequestException('SSO sign-in is expired or not found');
}
const issuer = await Issuer.discover(ssoSign.issuerUrl);
const client = new issuer.Client({
client_id: ssoSign.clientId,
client_secret: ssoSign.clientSecret,
redirect_uris: ssoSign.redirectUrls,
});
if (ssoSign === null) {
throw new BadRequestException('SSO sign-in is expired or not found');
}
const params = client.callbackParams(
`${callbackUrl}?code=${code}&state=${state}`,
);
try {
const tokenSet = await client.callback(callbackUrl, params, {
nonce: ssoSign.nonce,
code_verifier: ssoSign.codeVerifier,
state: ssoSign.state
});
await this.cacheService.delete(ssoSign.state);
return await lastValueFrom(
this.identityProviderService.SignInUser({
accessToken: tokenSet.access_token,
idToken: tokenSet.id_token,
refreshToken: tokenSet.refresh_token,
id: ssoSign.id,
}),
);
} catch (error) {
console.log(error);
throw error;
}
}
async deleteIdentityProvider(id: string, metadata: Metadata) {
return await lastValueFrom(
this.identityProviderService.DeleteIdentityProvider({ id }, metadata),
);
}
async updateIdentityProviders(
id: string,
body: CreateIdentityProvider,
metadata: Metadata,
) {
return await lastValueFrom(
this.identityProviderService.UpdateIdentityProvider(
{
id,
...body,
},
metadata,
),
);
}
async identityProvidersLinksPerDomain(frontDomain: string) {
return await lastValueFrom(
this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }),
);
}
}
+10 -156
View File
@@ -1,106 +1,5 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { Info } from 'protospack/dist/lib/interfaces';
export class GoogleAnalyticsClientSecrets {
@ApiProperty()
type: string;
@ApiProperty()
project_id: string;
@ApiProperty()
private_key_id: string;
@ApiProperty()
private_key: string;
@ApiProperty()
client_email: string;
@ApiProperty()
client_id: string;
@ApiProperty()
auth_uri: string;
@ApiProperty()
token_uri: string;
@ApiProperty()
auth_provider_x509_cert_url: string;
@ApiProperty()
client_x509_cert_url: string;
}
export class OauthObject {
@ApiPropertyOptional()
get_tokens_url: string;
@ApiPropertyOptional()
get_tokens_url_params: string;
@ApiPropertyOptional()
get_tokens_set_response: Record<string, any>;
@ApiPropertyOptional()
content_type: string;
}
export class InputOptions {
@ApiPropertyOptional()
oauth: OauthObject;
@ApiPropertyOptional()
skip_select_columns: true;
@ApiPropertyOptional()
skip_select_entities: false;
@ApiPropertyOptional()
skip_transformation: true;
}
export class FileFormatParams {
@ApiProperty()
sep: string;
@ApiProperty()
header: boolean;
@ApiProperty()
encoding: string;
}
export class AuthParameters {
@ApiProperty()
aws_access_key_id: string;
@ApiProperty()
aws_secret_access_key: string;
}
export class CredentialsJdbc {
@ApiPropertyOptional()
jdbc_user: string;
@ApiPropertyOptional()
jdbc_password: string;
@ApiPropertyOptional()
database: string;
@ApiPropertyOptional()
endpoint: string;
@ApiPropertyOptional()
port: string;
@ApiPropertyOptional()
engine: string;
@ApiPropertyOptional()
schema: string;
}
export class Credentials extends CredentialsJdbc {
@ApiProperty()
connection_type: string;
@ApiPropertyOptional()
client_aws_access_key_id: string;
@ApiPropertyOptional()
client_aws_secret_access_key: string;
@ApiPropertyOptional()
client_bucket: string;
@ApiPropertyOptional()
file_to_extract: string;
@ApiPropertyOptional()
file_format_params: FileFormatParams;
@ApiPropertyOptional()
view_id: string;
@ApiPropertyOptional()
client_secrets: GoogleAnalyticsClientSecrets;
@ApiPropertyOptional()
start_date: string;
@ApiPropertyOptional()
end_date: string;
@ApiPropertyOptional()
oauth_code: string;
}
export class Column {
@ApiProperty()
name: string;
@@ -114,6 +13,14 @@ export class TableColumns {
columns: string[];
@ApiProperty()
references: Column[];
@ApiProperty()
type: string;
}
export class AvailableEntity {
@ApiProperty()
name: string;
@ApiProperty()
replication_methods: string[];
}
export class Input {
@@ -133,26 +40,6 @@ export class Input {
created_at: string;
@ApiProperty()
updated_at: string;
//deprecated attributes (for retro compatibility)
@ApiPropertyOptional({ deprecated: true })
category: string;
@ApiPropertyOptional({ deprecated: true })
credentials: Credentials;
@ApiPropertyOptional({ deprecated: true })
options: InputOptions;
@ApiPropertyOptional({ deprecated: true })
info: Info;
@ApiPropertyOptional({ deprecated: true })
cron: string;
@ApiPropertyOptional({ deprecated: true })
source_bucket: string;
@ApiPropertyOptional({ deprecated: true })
source_prefix: string;
@ApiPropertyOptional({ deprecated: true })
auth_parameters: AuthParameters;
@ApiPropertyOptional({ deprecated: true })
file_format_params: FileFormatParams;
}
export class GetAvailableEntitiesReq {
@ApiProperty()
@@ -160,43 +47,10 @@ export class GetAvailableEntitiesReq {
}
export class GetAvailableEntitiesRes {
@ApiProperty()
entities: string[];
@ApiProperty({ type: [AvailableEntity] })
entities: AvailableEntity[];
}
export class TestConnectionGetColumnsReq {
@ApiProperty()
plugin: string;
@ApiProperty()
tables: string[];
@ApiPropertyOptional()
credentials: CredentialsJdbc;
@ApiPropertyOptional()
id: string;
@ApiPropertyOptional()
info: Info;
}
export class TestConnectionGetColumnsRes {
@ApiProperty({ type: [TableColumns] })
tables: TableColumns[];
}
export class TestConnectionReq {
@ApiProperty()
plugin: string;
@ApiProperty()
credentials: CredentialsJdbc;
@ApiPropertyOptional()
info: Info;
}
export class TestConnectionRes {
@ApiProperty()
connection_state: boolean;
@ApiProperty()
total_entities: number;
@ApiProperty()
database_tables: string[];
}
export class CreateInputReq extends OmitType(Input, [
'id',
'created_at',
+1 -1
View File
@@ -1,4 +1,4 @@
import { Info } from 'protospack/dist/lib/interfaces';
import { Info } from '@dadosfera/protospack/dist/lib/interfaces';
interface Values {
jdbc_user: string;
+5 -5
View File
@@ -2,10 +2,13 @@ import { Input } from '@dadosfera/protospack-v2';
import { credentials } from '@grpc/grpc-js';
import {
ClientProviderOptions,
GrpcOptions,
Transport,
type GrpcOptions,
} from '@nestjs/microservices';
const isLocalConnection =
process.env.INFACTORY_URL.startsWith('in-factory:') ||
process.env.INFACTORY_URL.includes('0.0.0.0');
export class InputsGrpcClient {
public readonly name = 'InputsGrpcClient';
private config: GrpcOptions = {
@@ -16,10 +19,7 @@ export class InputsGrpcClient {
Input.ProtoPackages.WritePackage,
Input.ProtoPackages.ReadPackage,
],
credentials:
process.env.LOCAL_ENV || process.env.ENV === 'local'
? undefined
: credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
Input.ProtoPaths.WriteFilePath,
Input.ProtoPaths.ReadFilePath,
+27 -42
View File
@@ -7,27 +7,26 @@ import {
Param,
Patch,
Post,
UseFilters,
} from '@nestjs/common';
import { InputsService } from './inputs.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { PERMISSIONS_GROUPS } from '../../authentication/permissions.enum';
import { AuthenticateCondition } from 'src/authentication/authentication.decorator';
import { AuthenticateCondition } from 'src/decorators/authentication.decorator';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import {
CreateInputReq,
GetAvailableEntitiesReq,
GetAvailableEntitiesRes,
Input,
TestConnectionGetColumnsReq,
TestConnectionGetColumnsRes,
TestConnectionReq,
TestConnectionRes,
} from './dtos/input.model';
import { UpdateInputRequest } from './dtos/old_interfaces';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('inputs')
@ApiTags('Inputs')
@Controller('inputs')
@AuthenticateCondition((req, user) => {
switch (req.method) {
@@ -38,7 +37,9 @@ import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entitie
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.CREATE.seqid,
) ||
user.permissions.includes(PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE.seqid)
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE.seqid,
)
);
default:
@@ -49,10 +50,13 @@ import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entitie
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.UPDATE.seqid,
) ||
user.permissions.includes(PERMISSIONS_GROUPS.PIPELINE.permissions.GET.seqid)
user.permissions.includes(
PERMISSIONS_GROUPS.PIPELINE.permissions.GET.seqid,
)
);
}
})
@UseFilters(new GrpcToHttpExceptionFilter())
export class InputsController {
logger: DadosferaLogger;
constructor(
@@ -63,44 +67,23 @@ export class InputsController {
this.logger = dadosferaLogger.logger;
}
@ApiInternalOnlyEndpoint()
@Get('available-entities/:plugin')
@ApiOkResponse({ type: GetAvailableEntitiesRes })
async getAvailableEntities(
@Body() body,
@Param() params: GetAvailableEntitiesReq,
@User() user: RequestUser,
) {
const { info } = body;
const { plugin } = params;
return await this.inputService.getAvailableEntities({ info, plugin });
}
@Post('/test-connection')
@ApiOkResponse({ type: TestConnectionRes })
async testConnection(@Body() data: TestConnectionReq) {
this.logger.info(`/test-connection - ON TEST CONNECTION ROUTE`, {
user: data.info.user_id,
customer: data.info.customer,
});
const response = await this.inputService.testConnection(data);
return response;
}
@Post('/test-connection/get-columns')
@ApiOkResponse({ type: TestConnectionGetColumnsRes })
async getColumns(@Body() data: TestConnectionGetColumnsReq) {
this.logger.info(
`/test-connection/get-columns - ON TEST CONNECTION GET COLUMNS ROUTE`,
{
user: data.info.user_id,
customer: data.info.customer,
const availableEntities = await this.inputService.getAvailableEntities({
info: {
customer: user.customer_name,
customer_id: user.customer_id,
user_id: user.user_id,
},
);
const response = await this.inputService.getColumns(data);
return response;
plugin,
});
return availableEntities;
}
@Post()
@@ -121,6 +104,7 @@ export class InputsController {
return response;
}
@ApiInternalOnlyEndpoint()
@Get()
async findAll(@User() user: RequestUser) {
const { user_id, customer_id, customer_name: customer } = user;
@@ -139,8 +123,7 @@ export class InputsController {
}
@Get('/:id')
async findOne(@Body() body, @Param() params) {
const { id } = params;
async findOne(@Body() body, @Param('id') id: string) {
this.logger.info(`/input/${id} - ON FIND ONE ROUTE`, {
user: body.info.user_id,
customer: body.info.customer,
@@ -151,6 +134,7 @@ export class InputsController {
return response;
}
@ApiInternalOnlyEndpoint()
@Patch(':id')
async update(@Body() updateInputDto: UpdateInputRequest, @Param() params) {
const { id } = params;
@@ -166,6 +150,7 @@ export class InputsController {
return response;
}
@ApiInternalOnlyEndpoint()
@Delete(':id')
async delete(@Body() data, @Param() params) {
const { id } = params;
+12 -83
View File
@@ -5,7 +5,6 @@ import {
Inject,
Injectable,
} from '@nestjs/common';
import { Timeout } from '@nestjs/schedule';
import CronParser, { CronExpression } from 'cron-parser';
import { ClientGrpc } from '@nestjs/microservices';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
@@ -14,10 +13,10 @@ import { objectCamelToSnake } from 'src/utils/CaseConverter';
import { IIdRequest, UpdateInputRequest } from './dtos/old_interfaces';
import { Input } from '@dadosfera/protospack-v2';
import {
GetAvailableEntitiesRequest,
InputCreateGenericRequest,
InputCreateS3Request,
InputNewCreateRequest,
TestConnectionGetColumnsRequest,
TestConnectionRequest,
} from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/messages';
import { Info } from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
@@ -72,17 +71,6 @@ export class InputsService {
objectCamelToSnake(createInputResponse);
return createInputResponse;
},
findOne: async (data: IIdRequest) => {
this.logger.info('InputClientService - FindOne');
const findOneInputResponse = await lastValueFrom(
this.inputReadService.InputFindOne(data),
).catch((e) => {
this.logger.error(e.details);
throw new HttpException(e.details, HttpStatus.INTERNAL_SERVER_ERROR);
});
objectCamelToSnake(findOneInputResponse);
return findOneInputResponse;
},
update: async (updateInputDTO: UpdateInputRequest) => {
this.logger.info('InputClientService - Update');
const updateInputResponse = await lastValueFrom(
@@ -91,14 +79,6 @@ export class InputsService {
return updateInputResponse;
},
remove: async (idRequest: IIdRequest) => {
this.logger.info('InputClientService - Remove');
const removeInputResponse = await lastValueFrom(
this.inputWriteService.InputRemove(idRequest),
);
return removeInputResponse;
},
testConnection: async (data: TestConnectionRequest) => {
this.logger.info('InputClientService - TestConnection');
const testConnectionResponse = await lastValueFrom(
@@ -107,32 +87,6 @@ export class InputsService {
return testConnectionResponse;
},
getColumns: async (data: TestConnectionGetColumnsRequest) => {
this.logger.info('InputClientService - TestConnection/Get-Columns');
const getColumnsResponse = await new Promise((resolve, reject) => {
this.inputReadService.GetColumns(data).subscribe({
next(x) {
resolve(objectCamelToSnake(x));
},
error(err) {
reject(err);
},
complete() {
// console.log('done');
},
});
})
.then((res) => {
this.logger.info('Done');
return res;
})
.catch((err) => {
this.logger.error(err.message);
throw new Error(err);
});
return getColumnsResponse;
},
getAvailableEntities: async (data) => {
this.logger.info('InputClientService - GetAvailableEntities');
data;
@@ -207,12 +161,9 @@ export class InputsService {
// this.validateCron(data);
const { body, info } = data;
if (body.info) delete body.info;
const inputCreateGenericRequest: any | InputCreateGenericRequest = {
const inputCreateGenericRequest: InputCreateGenericRequest = {
input: {
...body,
credentials: body.credentials && JSON.stringify(body.credentials),
},
info,
};
@@ -224,8 +175,8 @@ export class InputsService {
return { input: adjustedInput };
}
async getAvailableEntities(data): Promise<{ entities: string[] }> {
return await this.OLD_inputClient.getAvailableEntities(data);
async getAvailableEntities(data: GetAvailableEntitiesRequest) {
return lastValueFrom(this.inputReadService.GetAvailableEntities(data));
}
async findAll(info: Info) {
@@ -239,17 +190,13 @@ export class InputsService {
}
async findOne(idRequest: IIdRequest) {
try {
const findOneInputResponse: any = await this.OLD_inputClient.findOne(
idRequest,
);
findOneInputResponse.input = this.adjustInputPayload(
findOneInputResponse.input,
);
return findOneInputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
const findOneInputResponse: any = await lastValueFrom(
this.inputReadService.InputFindOne(idRequest),
);
findOneInputResponse.input = this.adjustInputPayload(
findOneInputResponse.input,
);
return findOneInputResponse;
}
async update(id: string, data, info: Info) {
@@ -271,16 +218,9 @@ export class InputsService {
}
async remove(idRequest: IIdRequest) {
try {
const removeInputResponse = await this.OLD_inputClient.remove(idRequest);
return removeInputResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
return lastValueFrom(this.inputWriteService.InputRemove(idRequest));
}
@Timeout(60000 * 10) // Timeout set for 10 minutes
async testConnection(data) {
try {
const testConnectionInputResponse =
@@ -292,17 +232,6 @@ export class InputsService {
}
}
async getColumns(data) {
try {
const testConnectionGetColumnsResponse =
await this.OLD_inputClient.getColumns(data);
return testConnectionGetColumnsResponse;
} catch (err) {
throw new HttpException(err.message, HttpStatus.NOT_FOUND);
}
}
generateInputS3Payload(payload) {
const { credentials, plugin, cron } = payload;
if (!credentials) return payload;
+101
View File
@@ -0,0 +1,101 @@
import { Body, Controller, Inject, Param, Post, Req } from '@nestjs/common';
import { init } from 'mixpanel';
import { Authenticated } from 'src/decorators/authentication.decorator';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
import { RequestUser, User } from 'src/decorators/user.decorator';
@ApiInternalOnlyController()
@Authenticated()
@Controller('trackEvent')
export class MixpanelController {
constructor(
@Inject('MIXPANEL_TOKEN')
private readonly mixpanelToken: string,
) {}
@Post(':id')
async trackEvent(
@Param('id') id,
@Body() body,
@User() user: RequestUser,
@Req() request
) {
delete body.info;
const mixpanel = init(this.mixpanelToken);
const separator = user.username.includes('-') ? '-' : '.';
const removeValues = [
'.dadosferatech.dadosfera',
'.demo.dadosfera',
'.dadosferademo',
'.dadosferarh.dadosfera',
'.dadosferatech.dadosfera2',
'.dadosferatech.dadosfera',
'.dadosfera.fin',
'.dadosferafin.dadosfera',
'.praxio.dadosfera',
'.dadosfera.tech',
'.treinamentos@dadosfera.ai',
'.dadosfera2',
'.treinamentosfera',
'.dadosfera',
];
let username = user.username;
removeValues.forEach((value) => {
username = username.replace(value, '');
});
username = username.split('@')?.[0];
username = username.split('+')?.[0];
let firstName = username
.substring(0, username.indexOf(separator))
.replace('dadosfera', '');
let lastName = username
.substring(username.lastIndexOf(separator) + 1)
.replace('dadosfera', '');
if (!firstName) {
firstName = lastName;
lastName = '';
}
firstName = this.capitalize(firstName);
lastName = this.capitalize(lastName);
await mixpanel.people.set(user.username, {
$first_name: firstName,
$last_name: lastName,
$name: this.getFullName(firstName, lastName),
$email: user.username.includes('@')
? user.username
: user.username + '@dadosfera.ai',
customer_name: user.customer_name
});
await mixpanel.track(id, {
distinct_id: user.username,
customer: user.customer_name,
env: process.env.ENV,
$ip: request.ip,
$os: request.headers['sec-ch-ua-platform'] || '',
$browser: request.headers['user-agent'],
...body,
});
return { id, body, user: user.username };
}
capitalize(sentence: string): string {
if (!sentence) {
return '';
}
return sentence[0].toUpperCase() + sentence.substring(1);
}
getFullName(firstName: string, lastName: string) {
return `${firstName}${lastName ? ' ' + lastName : ''}`;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { getSecretFromSecretsManager } from 'src/utils/SecretManager';
import { MixpanelController } from './mixpanel.controller';
@Module({
controllers: [MixpanelController],
providers: [
{
provide: 'MIXPANEL_TOKEN',
useValue: getSecretFromSecretsManager(
`${process.env.ENV}/root/mixpanel_token`,
),
},
],
})
export class MixpanelModule {}
@@ -6,6 +6,10 @@ import {
} from '@nestjs/microservices';
import { NetworkConfig } from '@dadosfera/protospack-v2';
const isLocalConnection =
process.env.INFACTORY_URL.startsWith('in-factory:') ||
process.env.INFACTORY_URL.includes('0.0.0.0');
export class NetworkConfigGrpcClient {
public name = 'NetworkConfigGrpcClient';
@@ -17,8 +21,7 @@ export class NetworkConfigGrpcClient {
NetworkConfig.ProtoPackages.ReadPackage,
NetworkConfig.ProtoPackages.WritePackage,
],
credentials:
process.env.ENV === 'local' ? undefined : credentials.createSsl(),
credentials: isLocalConnection ? undefined : credentials.createSsl(),
protoPath: [
NetworkConfig.ProtoPaths.ReadFilePath,
NetworkConfig.ProtoPaths.WriteFilePath,
@@ -1,12 +1,9 @@
import {
Body,
Controller,
Delete,
Get,
Headers,
Inject,
Param,
Patch,
Post,
Query,
UploadedFile,
@@ -15,7 +12,7 @@ import {
import { NetworkConfigService } from './network-config.service';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ApiConsumes, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { RequestUser, User } from 'src/decorators/user.decorator';
import {
CreateNetworkConfigReq,
CreateNetworkConfigRes,
@@ -26,7 +23,9 @@ import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
} from 'src/decorators/authentication.decorator';
import { Language } from 'src/decorators/language.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
const networkConfigPermissions = PERMISSIONS_GROUPS.NETWORK_CONFIG.permissions;
@ApiTags('Network Config')
@@ -40,18 +39,18 @@ export class NetworkConfigController {
) {
this.logger = dadosferaLogger.logger;
}
@Get()
@ApiOkResponse({ type: FindAllNetworkConfigsRes })
async findAllNetworkConfigs(
@User() user: RequestUser,
@Headers('Dadosfera-Lang') language,
@Language() language: LanguageEnum,
@Query() queries,
) {
this.logger.info('GET /network-config', {
user: user.user_id,
customer: user.customer_name,
});
if (!language) language = 'en-us';
const { user_id, customer_id, customer_name } = user;
const { search, size, page, ...filters } = queries;
@@ -79,14 +78,14 @@ export class NetworkConfigController {
@ApiOkResponse({ type: FindAllNetworkConfigsRes })
async findNetworkConfigByIdDetails(
@User() user: RequestUser,
@Headers('Dadosfera-Lang') language,
@Language() language: LanguageEnum,
@Param('id') id: string,
) {
this.logger.info('GET /network-config/:id', {
user: user.user_id,
customer: user.customer_name,
});
if (!language) language = 'en-us';
return this.networkConfigService.findNetworkConfigByIdDetails(id, {
customer_name: user.customer_name,
customer_id: user.customer_id,
@@ -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,
),
);
}
}
+20 -10
View File
@@ -5,9 +5,8 @@ import { ConnectionClientService } from '../connection/client.service';
import jwt from 'jsonwebtoken';
import DadosferaLogger from '@dadosfera/dadosfera-logs/dist';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { Authenticated } from 'src/authentication/authentication.decorator';
import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator';
@ApiTags('oauth')
@Authenticated()
@Controller('oauth')
export class OauthController {
redirectUrl: string;
@@ -17,15 +16,14 @@ export class OauthController {
private logger: DadosferaLogger,
) {
switch (process.env.ENV) {
case 'dev':
case 'stg':
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/coletix/auth-callback`;
this.redirectUrl = `https://app.${process.env.ENV}.dadosfera.ai/collect/auth-callback`;
break;
case 'prd':
this.redirectUrl = `https://app.dadosfera.ai/coletix/auth-callback`;
this.redirectUrl = `https://app.dadosfera.ai/collect/auth-callback`;
break;
default:
this.redirectUrl = `http://localhost:4200/coletix/auth-callback`;
this.redirectUrl = `http://localhost:4200/collect/auth-callback`;
}
}
@Get('hubspot')
@@ -34,6 +32,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('hubspot/callback')
@UseGuards(AuthGuard('hubspot'))
@Redirect()
@@ -47,6 +46,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('googleads/callback')
@UseGuards(AuthGuard('google'))
@Redirect()
@@ -60,6 +60,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('google-sheets/callback')
@UseGuards(AuthGuard('google'))
@Redirect()
@@ -73,6 +74,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('facebook/callback')
@UseGuards(AuthGuard('facebook'))
@Redirect()
@@ -86,6 +88,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('mailchimp/callback')
@UseGuards(AuthGuard('mailchimp'))
@Redirect()
@@ -99,6 +102,7 @@ export class OauthController {
return true;
}
@ApiInternalOnlyEndpoint()
@Get('salesforce/callback')
@UseGuards(AuthGuard('salesforce'))
@Redirect()
@@ -150,10 +154,16 @@ export class OauthController {
const { customer_id, customer_name, user_id } = connectionInfo;
try {
const response = await this.connectionService.createConnection(
connectionInfo,
PackTheMetadata({ customer_id, customer_name, user_id }),
);
const response = connectionInfo.id
? await this.connectionService.updateConnection(
connectionInfo.id,
connectionInfo,
PackTheMetadata({ customer_id, customer_name, user_id }),
)
: await this.connectionService.createConnection(
connectionInfo,
PackTheMetadata({ customer_id, customer_name, user_id }),
);
url.searchParams.set('connection_id', response.connection.id);
return { url: url.href };
} catch (error) {
+2
View File
@@ -23,6 +23,7 @@ export class OauthService {
user_id,
customer_id,
customer_name,
id,
} = query;
const properties = { plugin, credentials_type: 'oauth' };
@@ -45,6 +46,7 @@ export class OauthService {
type,
properties,
plugin,
id,
};
return jwt.sign({ ...newConnection }, process.env.JWT_PRIVATE_KEY);
}
@@ -44,7 +44,8 @@ export class FacebookStrategy extends PassportStrategy(Strategy) {
req,
'application',
);
options.scope = 'ads_read ads_management';
options.scope =
'pages_show_list ads_read pages_read_engagement ads_management';
super.authenticate(req, options);
}
}
@@ -29,7 +29,6 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
super(options, verify);
switch (process.env.ENV) {
case 'dev':
case 'stg':
this.redirect_uri = `https://maestro.${process.env.ENV}.dadosfera.ai/oauth`;
break;
@@ -17,7 +17,7 @@ export class HubspotStrategy extends PassportStrategy(Strategy) {
clientSecret: oauthSecrets.hubspot.client_secret,
callbackURL: oauthSecrets.hubspot.redirect_uri,
scope:
'automation business-intelligence oauth forms integration-sync sales-email-read crm.lists.read crm.objects.contacts.read crm.schemas.contacts.read crm.objects.companies.read crm.objects.deals.read crm.schemas.companies.read crm.schemas.deals.read crm.objects.owners.read crm.objects.quotes.read crm.schemas.quotes.read crm.objects.line_items.read crm.schemas.line_items.read',
'tickets automation business-intelligence oauth forms integration-sync sales-email-read crm.lists.read crm.objects.contacts.read crm.schemas.contacts.read crm.objects.companies.read crm.objects.deals.read crm.schemas.companies.read crm.schemas.deals.read crm.objects.owners.read crm.objects.quotes.read crm.schemas.quotes.read crm.objects.line_items.read crm.schemas.line_items.read',
passReqToCallback: true,
},
(accessToken, refreshToken, tokenInfo, profile, done) => {
@@ -0,0 +1,48 @@
import { ApiProperty } from "@nestjs/swagger";
export class CreateUserOpenDataDTO {
@ApiProperty()
firstName: string;
@ApiProperty()
lastName: string;
@ApiProperty()
email: string;
@ApiProperty()
organization: string;
@ApiProperty()
enquiryType: string;
}
type FormField = {
id: string;
type: string;
title: string;
value: string;
raw_value: string;
required: string;
};
type MetaData = {
title: string;
value: string;
};
export type WordpressForm = {
form: {
id: string;
name: string;
};
fields: {
[key: string]: FormField;
};
meta: {
date: MetaData;
time: MetaData;
page_url: MetaData;
user_agent: MetaData;
remote_ip: MetaData;
credit: MetaData;
};
};
@@ -0,0 +1,101 @@
import DadosferaLogger from '@dadosfera/dadosfera-logs';
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 { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { LanguageEnum } from 'src/utils/languages.enum';
import { UsersService } from '../users/users.service';
import { Language } from 'src/decorators/language.decorator';
import { OpenDataService } from './open-data.service';
import { CreateUserOpenDataDTO, WordpressForm } from './dto/wordpres-form';
import { Metadata } from '@grpc/grpc-js';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import { request } from 'http';
import { Request } from 'express';
@Controller('open-data')
@ApiInternalOnlyController()
@ApiTags('OpenData')
@ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }])
@UseFilters(GrpcToHttpExceptionFilter)
export class OpenDataController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private openDataService: OpenDataService,
) {
this.logger = dadosferaLogger.logger;
}
@Post("/sharing-ocean-data")
@HttpCode(200)
@Header('content-type', 'application/json')
@ApiOkResponse()
async createUser(
@Body()
body: WordpressForm,
@Query('language')
language: string,
@Req()
request: Request,
@Headers('origin')
origin: string
) {
this.logger.info('createUser for open data' + JSON.stringify(request.headers));
// const corslist = ["https://devsbm.dadosfera.io", "https://sharingoceandata.com"];
// if (!corslist.includes(origin)) {
// this.logger.info('block request by cors list: '+ origin);
// throw new ForbiddenException();
// }
const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID;
const OPENDATA_GROUP_ID = process.env.OPEN_GROUP_ID;
const roles = [process.env.OPEN_GROUP_ID];
const metadata = PackTheMetadata({
language: language || 'en-us'
});
const data = {}
try {
Object.keys(body.fields)
.filter(key => body.fields[key].required === "1")
.forEach(key => {
const field = body.fields[key]
data[field.id] = field.value
});
} catch (e) {
this.logger.error('user data ' + e.message);
}
const user: CreateUserOpenDataDTO = {
email: data["email"],
enquiryType: data["enquiry_type"],
firstName: data["first_name"],
lastName: data["last_name"],
organization: data["organization"]
}
this.logger.info(`user request to group ${OPENDATA_CUSTOMER_ID} with role ${OPENDATA_GROUP_ID}`);
try {
const id = await this.openDataService.createUser(OPENDATA_CUSTOMER_ID, user, roles, metadata);
this.logger.info('user created with id: '+ id);
return {
success: true,
status: 'success',
message: 'user created with succesfull'
}
} catch (e) {
this.logger.error('failed with exception: ' + e.message);
return {
success: false,
status: 'failed',
message: e.message
};
}
}
}
+22
View File
@@ -0,0 +1,22 @@
import { Module } from '@nestjs/common';
import { OpenDataController } from './open-data.controller';
import { UsersService } from '../users/users.service';
import { ClientsModule } from '@nestjs/microservices'
import { DucClient } from '../duc/client.config';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { RolesModule } from '../roles/roles.module';
import { PermissionsModule } from '../permissions/permissions.module';
import { OpenDataService } from './open-data.service';
const client = new DucClient();
@Module({
controllers: [OpenDataController],
imports: [
ClientsModule.register([client.providerOptions]),
RolesModule,
// PermissionsModule,
],
providers: [DadosferaLogger, UsersService, OpenDataService]
})
export class OpenDataModule {}
@@ -0,0 +1,51 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { lastValueFrom } from 'rxjs';
import { CreateUserOpenDataDTO } from './dto/wordpres-form';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { UsersProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { DucClient } from '../duc/client.config';
import { ClientGrpc } from '@nestjs/microservices';
import { ProtoServices } from '@dadosfera/protospack-v2/dist/lib/Duc';
import { Metadata } from '@grpc/grpc-js';
@Injectable()
export class OpenDataService implements OnModuleInit {
logger: DadosferaLogger;
private usersClientService: UsersProtoService;
constructor(
@Inject(DadosferaLogger)
private dadosferaLogger: DadosferaLogger,
@Inject(DucClient.name)
private readonly grpcClient: ClientGrpc,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.usersClientService = this.grpcClient.getService(
ProtoServices.UsersProtoService,
);
}
async createUser(customerId: string, data: CreateUserOpenDataDTO, roleIds: string[], metadata: Metadata) {
const body = {
email: data.email,
name: data.firstName + " " + data.lastName,
department: data.organization,
jobTitle: data.enquiryType,
customerId: customerId,
roleIds: roleIds
}
try {
const { user } = await lastValueFrom(
this.usersClientService.SimpleUserCreate(body, metadata),
);
return user.id;
} catch(err) {
return err;
}
}
}
@@ -1,19 +1,16 @@
import { Controller, Get, Headers, Inject } from '@nestjs/common';
import { Controller, Get, Inject } from '@nestjs/common';
import { ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Authenticated,
RequireAllPermissions,
} from 'src/authentication/authentication.decorator';
import {
PERMISSIONS_GROUPS,
PermissionUsages,
} from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/authentication/user.decorator';
import { Authenticated } from 'src/decorators/authentication.decorator';
import { PermissionUsages } from 'src/authentication/permissions.enum';
import { RequestUser, User } from 'src/decorators/user.decorator';
import { LanguageEnum } from 'src/utils/languages.enum';
import { GetPublicPermissionsRes } from './dto/entities';
import { PermissionsService } from './permissions.service';
import { Language } from 'src/decorators/language.decorator';
import { ApiInternalOnlyController } from 'src/decorators/swagger.decorator';
@ApiInternalOnlyController()
@ApiTags('Permissions')
@Authenticated()
@Controller('permissions')
@@ -38,10 +35,10 @@ export class PermissionsController {
@ApiOkResponse({ type: GetPublicPermissionsRes })
getPublicPermissions(
@User() user: RequestUser,
@Headers('dadosfera-lang') language,
@Language() language: LanguageEnum,
) {
this.logger.info('getPublicPermissions', { user });
if (!language) language = 'en-us';
const permissionGroups = this.permissionsService.getPermissionsGrouped(
PermissionUsages.PUBLIC,
language,
@@ -44,8 +44,11 @@ export class PermissionsService
);
}
// internally used to send permissions to duc on microservice startup
async onApplicationBootstrap() {
// Do not sent permissions if running local but pointing to a remote DUC url
if (process.env.ENV == 'local' && !process.env.DUC_URL.includes('0.0.0.0'))
return;
this.logger.info('sending permissions to DUC...');
const permissions = Object.values(PERMISSIONS_GROUPS).flatMap((namespace) =>
@@ -57,10 +60,10 @@ export class PermissionsService
}).catch((err: ErrorBuilder) => {
if (
err.code === 'No connection established' &&
(process.env.LOCAL_ENV === 'true' || process.env.ENV === 'local')
process.env.ENV === 'local'
) {
return this.logger.info(
"couldn't connect to DUC. suppresing in local env",
"couldn't connect to DUC. suppressing in local env",
);
}

Some files were not shown because too many files have changed in this diff Show More