Compare commits

...
17 changed files with 471 additions and 357 deletions
-73
View File
@@ -1,73 +0,0 @@
name: Deploy K8S Modifications
on:
push:
branches:
- main
- beta
jobs:
extract_environment:
runs-on: ubuntu-22.04
outputs:
environment: ${{ steps.extract_environment.outputs.environment }}
steps:
- name: Extract Environment
run: |
if [ ${GITHUB_REF} == "refs/heads/main" ]; then
echo "environment=prd" >> $GITHUB_OUTPUT
elif [ ${GITHUB_REF} == "refs/heads/beta" ]; then
echo "environment=stg" >> $GITHUB_OUTPUT
fi
id: extract_environment
helmfile-deploy:
needs: [extract_environment]
runs-on: [self-hosted, "prd-azure"]
steps:
- 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 Apply
env:
ENV: ${{ needs.extract_environment.outputs.environment }}
run: helmfile -f helmfiles/${ENV}.yaml sync
+1
View File
@@ -2,6 +2,7 @@
<image src="./assets/maestro.svg" style="width:10rem"> <image src="./assets/maestro.svg" style="width:10rem">
</p> </p>
# Maestro # Maestro
Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços. Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços.
+110 -153
View File
@@ -5371,92 +5371,6 @@
] ]
} }
}, },
"/customers/{id}/theme": {
"post": {
"operationId": "CustomersController_saveCustomertheme",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeRequest"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeResponse"
}
}
}
},
"201": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeResponse"
}
}
}
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
},
"get": {
"operationId": "CustomersController_getCustomerTheme",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeResponse"
}
}
}
}
},
"tags": [
"Customers"
]
}
},
"/customers/token": { "/customers/token": {
"get": { "get": {
"operationId": "CustomersController_getCustomerToken", "operationId": "CustomersController_getCustomerToken",
@@ -5517,20 +5431,6 @@
] ]
} }
}, },
"/health": {
"get": {
"operationId": "HealthController_check",
"parameters": [],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Health"
]
}
},
"/open-data/sharing-ocean-data": { "/open-data/sharing-ocean-data": {
"post": { "post": {
"operationId": "OpenDataController_createUser", "operationId": "OpenDataController_createUser",
@@ -5546,6 +5446,37 @@
], ],
"type": "string" "type": "string"
} }
},
{
"name": "language",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"OpenData"
]
}
},
"/customers/{id}/theme": {
"post": {
"operationId": "ThemeController_saveCustomertheme",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
} }
], ],
"requestBody": { "requestBody": {
@@ -5553,18 +5484,75 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/CreateUserOpenDataDTO" "$ref": "#/components/schemas/CustomerThemeRequest"
} }
} }
} }
}, },
"responses": { "responses": {
"201": { "200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeResponse"
}
}
}
}
},
"tags": [
"Theme"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
},
"get": {
"operationId": "ThemeController_getCustomerTheme",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CustomerThemeResponse"
}
}
}
}
},
"tags": [
"Theme"
]
}
},
"/health": {
"get": {
"operationId": "HealthController_check",
"parameters": [],
"responses": {
"200": {
"description": "" "description": ""
} }
}, },
"tags": [ "tags": [
"OpenData" "Health"
] ]
} }
} }
@@ -8344,27 +8332,37 @@
"links" "links"
] ]
}, },
"CustomerLinkRequest": {
"type": "object",
"properties": {
"links": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CustomerLink"
}
}
},
"required": [
"links"
]
},
"CustomerThemeRequest": { "CustomerThemeRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
"displayName": {
"type": "string"
},
"backgroundColor": { "backgroundColor": {
"type": "string" "type": "string"
}, },
"textColor": { "textColor": {
"type": "string" "type": "string"
},
"logoWhite": {
"type": "string"
},
"logoBlack": {
"type": "string"
} }
}, },
"required": [ "required": [
"displayName",
"backgroundColor", "backgroundColor",
"textColor", "textColor"
"logoWhite",
"logoBlack"
] ]
}, },
"CustomerTheme": { "CustomerTheme": {
@@ -8400,47 +8398,6 @@
"required": [ "required": [
"theme" "theme"
] ]
},
"CustomerLinkRequest": {
"type": "object",
"properties": {
"links": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CustomerLink"
}
}
},
"required": [
"links"
]
},
"CreateUserOpenDataDTO": {
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"email": {
"type": "string"
},
"organization": {
"type": "string"
},
"enquiryType": {
"type": "string"
}
},
"required": [
"firstName",
"lastName",
"email",
"organization",
"enquiryType"
]
} }
} }
} }
+7 -6
View File
@@ -9,13 +9,14 @@ charts:
- name: hostname - name: hostname
value: maestro.stg.dadosfera.ai value: maestro.stg.dadosfera.ai
- name: maestro.pi_factory_url - name: maestro.pi_factory_url
value: pi-factory.stg.dadosfera.ai value: pi-factory.dadosfera.ai
- name: maestro.in_factory_url - name: maestro.in_factory_url
value: in-factory.stg.dadosfera.ai value: in-factory.dadosfera.ai
- name: maestro.tr_factory_url - name: maestro.tr_factory_url
value: in-factory.stg.dadosfera.ai value: in-factory.dadosfera.ai
- name: maestro.open_customer_id - name: maestro.open_customer_id
value: bb4e9b26-d465-40ff-9345-e768ca69a55e value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d
- name: maestro.open_group_id - name: maestro.open_group_id
value: 4d621501-5ea4-444b-b46e-1a009f3132ff value: e3f98a2f-7748-4981-8505-7695c8ca8218
- name: replicaCount
value: 1
+5
View File
@@ -36,6 +36,11 @@ spec:
operator: In operator: In
values: values:
- backend - backend
tolerations:
- key: "kubernetes.azure.com/scalesetpriority"
operator: "Equal"
value: "spot"
effect: "NoSchedule"
containers: containers:
- name: maestro - name: maestro
+9 -8
View File
@@ -12,7 +12,7 @@
"@aws-sdk/client-secrets-manager": "^3.414.0", "@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3", "@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.37.0-beta.1", "@dadosfera/protospack-v2": "3.37.0-beta.3",
"@grpc/grpc-js": "^1.9.3", "@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9", "@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0", "@nestjs/cli": "^9.5.0",
@@ -53,7 +53,7 @@
"@types/jest": "27.0.2", "@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.9", "@types/jsonwebtoken": "^8.5.9",
"@types/jwk-to-pem": "^2.0.1", "@types/jwk-to-pem": "^2.0.1",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.12",
"@types/node": "^16.18.52", "@types/node": "^16.18.52",
"@types/passport-facebook": "^2.1.11", "@types/passport-facebook": "^2.1.11",
"@types/passport-google-oauth20": "^2.0.11", "@types/passport-google-oauth20": "^2.0.11",
@@ -1403,9 +1403,9 @@
} }
}, },
"node_modules/@dadosfera/protospack-v2": { "node_modules/@dadosfera/protospack-v2": {
"version": "3.37.0-beta.1", "version": "3.37.0-beta.3",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.37.0-beta.1.tgz", "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.37.0-beta.3.tgz",
"integrity": "sha512-AcDxeg7KruBJncOXbLJFHKH/5GQ9VHR8MxU7s1o1lrZVfZY4Ht3UWmvwL1F2b+6DIhTDqzVOsKjfg7KAbuDYuw==", "integrity": "sha512-9YSgzkXVqe9QsBxJjfxYxttAiyCXxgq49Xjs3OvnTLCXRDn3XtTlPaC+yHrTEUgCkQyhlDZuswe6f6oBImqIRQ==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@grpc/grpc-js": "^1.9.3", "@grpc/grpc-js": "^1.9.3",
@@ -3527,10 +3527,11 @@
"dev": true "dev": true
}, },
"node_modules/@types/multer": { "node_modules/@types/multer": {
"version": "1.4.11", "version": "1.4.12",
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz",
"integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@types/express": "*" "@types/express": "*"
} }
+2 -2
View File
@@ -30,7 +30,7 @@
"@aws-sdk/client-secrets-manager": "^3.414.0", "@aws-sdk/client-secrets-manager": "^3.414.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3", "@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "3.37.0-beta.1", "@dadosfera/protospack-v2": "3.37.0-beta.3",
"@grpc/grpc-js": "^1.9.3", "@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9", "@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0", "@nestjs/cli": "^9.5.0",
@@ -74,7 +74,7 @@
"@types/jest": "27.0.2", "@types/jest": "27.0.2",
"@types/jsonwebtoken": "^8.5.9", "@types/jsonwebtoken": "^8.5.9",
"@types/jwk-to-pem": "^2.0.1", "@types/jwk-to-pem": "^2.0.1",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.12",
"@types/node": "^16.18.52", "@types/node": "^16.18.52",
"@types/passport-facebook": "^2.1.11", "@types/passport-facebook": "^2.1.11",
"@types/passport-google-oauth20": "^2.0.11", "@types/passport-google-oauth20": "^2.0.11",
+4 -2
View File
@@ -27,6 +27,7 @@ import { ProductboardModule } from './modules/productboard/productboard.module';
import { MixpanelModule } from './modules/mixpanel/mixpanel.module'; import { MixpanelModule } from './modules/mixpanel/mixpanel.module';
import { CustomersModule } from './modules/customers/customers.module'; import { CustomersModule } from './modules/customers/customers.module';
import { OpenDataModule } from './modules/open-data/open-data.module'; import { OpenDataModule } from './modules/open-data/open-data.module';
import { ThemeModule } from './modules/theme/theme.module';
@Module({ @Module({
providers: [ providers: [
@@ -58,9 +59,10 @@ import { OpenDataModule } from './modules/open-data/open-data.module';
ProductboardModule, ProductboardModule,
MixpanelModule, MixpanelModule,
CustomersModule, CustomersModule,
//Always leave HealthModule last, so it is on the bottom of swagger
HealthModule,
OpenDataModule, OpenDataModule,
ThemeModule,
//Always leave HealthModule last, so it is on the bottom of swagger
HealthModule
], ],
}) })
export class AppModule {} export class AppModule {}
+1 -50
View File
@@ -1,15 +1,12 @@
import { DadosferaLogger } from '@dadosfera/dadosfera-logs'; import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { IdResponse } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { import {
Body, Body,
Controller, Controller,
Get, Get,
HttpCode, HttpCode,
HttpException,
HttpStatus, HttpStatus,
Inject, Inject,
Param, Param,
Post,
Put, Put,
Query, Query,
UseFilters, UseFilters,
@@ -22,11 +19,10 @@ import {
} from 'src/decorators/authentication.decorator'; } from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter'; import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomersService } from './customers.service'; import { CustomersService } from './customers.service';
import { CustomerLinkRequest, CustomerLinksResponse, CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers'; import { CustomerLinkRequest, CustomerLinksResponse } from './dtos/customers';
import { RequestUser, User } from 'src/decorators/user.decorator'; import { RequestUser, User } from 'src/decorators/user.decorator';
import type { StringValue } from 'ms'; import type { StringValue } from 'ms';
import { PackTheMetadata } from 'src/utils/ PackTheMetadata'; import { PackTheMetadata } from 'src/utils/ PackTheMetadata';
import ErrorCodes from 'src/utils/errorCodes';
@ApiTags('Customers') @ApiTags('Customers')
@Controller('customers') @Controller('customers')
@@ -51,51 +47,6 @@ export class CustomersController {
return { links }; return { links };
} }
@Post(':id/theme')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: CustomerThemeResponse })
async saveCustomertheme(@Param('id') id: string, @Body() data: CustomerThemeRequest) {
this.logger.info('saveCustomertheme', JSON.stringify({
id, theme: data
}));
try {
// const theme = await this.customersService.createThemeByCustomer(id, data);
this.logger.info('saveCustomertheme', JSON.stringify(data));
return { theme: null };
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - saveCustomertheme - Expect CUSTOMER.NOT_FOUND');
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
} else {
this.logger.error('Error - saveCustomertheme Unknown Error:' + err?.message);
return { theme: null };
};
}
}
@Get(':id/theme')
@ApiOkResponse({ type: CustomerThemeResponse })
async getCustomerTheme(@Param('id') id: string) {
this.logger.info('getCustomerTheme with id' + id);
try {
const data = await this.customersService.getThemeByCustomer(id);
this.logger.info('Success - getCustomerTheme with id'+ id);
return data;
}catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - getCustomerTheme - Expect CUSTOMER.NOT_FOUND');
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
} else {
this.logger.error('Error - getCustomerTheme Unknown Error:' + err?.message);
return { theme: null };
};
}
}
@Put(':id/links') @Put(':id/links')
@Authenticated() @Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN) @RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@@ -28,7 +28,6 @@ import {
} from '@dadosfera/protospack-v2/dist/lib/PipelineV2'; } from '@dadosfera/protospack-v2/dist/lib/PipelineV2';
import { Metadata } from '@grpc/grpc-js'; import { Metadata } from '@grpc/grpc-js';
import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client'; import { PipelinesClientConfiguration } from '../pipelinesV2/pipelines-client';
import { CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers';
import DadosferaLogger from '@dadosfera/dadosfera-logs'; import DadosferaLogger from '@dadosfera/dadosfera-logs';
// This function will accept any string, which may result in a bug. // This function will accept any string, which may result in a bug.
@@ -90,39 +89,6 @@ export class CustomersService implements OnModuleInit {
} }
} }
async createThemeByCustomer(id: string, theme: CustomerThemeRequest): Promise<CustomerThemeResponse> {
if (!id) {
this.logger.error('Error - saveCustomertheme - not found id:' + id);
throw new HttpException(null, HttpStatus.BAD_REQUEST);
}
const result = await firstValueFrom(
this.customerService.CustomerCreateTheme({
customerId: id,
theme
}),
);
return { theme: result };
}
async getThemeByCustomer(id: string): Promise<CustomerThemeResponse> {
if (!id) {
this.logger.error('Error - getCustomerTheme - not found id:' + id);
throw new HttpException(null, HttpStatus.BAD_REQUEST);
}
const result = await firstValueFrom(
this.customerService.CustomerGetTheme({
id
}),
);
return {
theme: result
}
}
async generateToken( async generateToken(
expiresIn = '30m', expiresIn = '30m',
data: { customerId: string; userId: string; customerName: string }, data: { customerId: string; userId: string; customerName: string },
+1 -27
View File
@@ -1,4 +1,4 @@
import { Link, Theme } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities'; import { Link } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/entities';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CustomerLink implements Link { export class CustomerLink implements Link {
@@ -21,29 +21,3 @@ export class CustomerLinksResponse {
links: CustomerLink[]; links: CustomerLink[];
} }
export class CustomerTheme implements Theme {
@ApiProperty()
backgroundColor: string;
@ApiProperty()
textColor: string;
@ApiProperty()
logoWhite: string;
@ApiProperty()
logoBlack: string;
}
export class CustomerThemeResponse {
@ApiProperty()
theme: CustomerTheme;
}
export class CustomerThemeRequest {
@ApiProperty()
backgroundColor: string;
@ApiProperty()
textColor: string;
@ApiProperty()
logoWhite: string;
@ApiProperty()
logoBlack: string;
}
+2
View File
@@ -29,6 +29,8 @@ export class DucClient {
objects: true, objects: true,
arrays: true, arrays: true,
}, },
maxSendMessageLength: 15 * 1024 * 1024, // 15 MB por mensagem
maxReceiveMessageLength: 15 * 1024 * 1024,
}, },
}; };
@@ -55,9 +55,11 @@ export class OpenDataController {
// const OPENDATA_PUBLIC_USERS_GROUP_ID = process.env.OPEN_GROUP_ID; // const OPENDATA_PUBLIC_USERS_GROUP_ID = process.env.OPEN_GROUP_ID;
// ""f239718a-a271-4ef9-ae7e-02a2f0f3aa6e"" // ""f239718a-a271-4ef9-ae7e-02a2f0f3aa6e""
// const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID; // const OPENDATA_CUSTOMER_ID = process.env.OPEN_CUSTOMER_ID;
const OPENDATA_CUSTOMER_ID = "bb4e9b26-d465-40ff-9345-e768ca69a55e"; const OPENDATA_CUSTOMER_ID = "b3e3dfe5-b992-4586-a73c-c0b0c00f615d";
const roles = ["4d621501-5ea4-444b-b46e-1a009f3132ff"]; this.logger.info("OPENDATA_CUSTOMER_ID: " + process.env.OPEN_CUSTOMER_ID)
this.logger.info("OPEN_GROUP_ID: " + process.env.OPEN_GROUP_ID)
const roles = ["e3f98a2f-7748-4981-8505-7695c8ca8218"];
const metadata = PackTheMetadata({ const metadata = PackTheMetadata({
language: language || 'en-us' language: language || 'en-us'
}); });
+47
View File
@@ -0,0 +1,47 @@
import { Link, Theme } 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[];
}
export class CustomerTheme implements Theme {
@ApiProperty()
backgroundColor: string;
@ApiProperty()
textColor: string;
@ApiProperty()
logoWhite: string;
@ApiProperty()
logoBlack: string;
}
export class CustomerThemeResponse {
@ApiProperty()
theme: CustomerTheme;
}
export class CustomerThemeRequest {
@ApiProperty()
displayName: string;
@ApiProperty()
backgroundColor: string;
@ApiProperty()
textColor: string;
}
+125
View File
@@ -0,0 +1,125 @@
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Inject,
Param,
Post,
Put,
Query,
UploadedFiles,
UseFilters,
UseInterceptors,
HttpCode
} from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { PERMISSIONS_GROUPS } from 'src/authentication/permissions.enum';
import {
Authenticated,
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers';
import ErrorCodes from 'src/utils/errorCodes';
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import { ThemeService } from './theme.service';
@ApiTags('Theme')
@Controller('customers')
@UseFilters(GrpcToHttpExceptionFilter)
export class ThemeController {
logger: DadosferaLogger;
constructor(
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
private themeService: ThemeService,
) {
this.logger = dadosferaLogger.logger;
}
@Post('/:id/theme')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse({ type: CustomerThemeResponse })
@UseInterceptors(AnyFilesInterceptor())
@HttpCode(HttpStatus.OK)
async saveCustomertheme(
@Param('id') id: string,
@Body() data: CustomerThemeRequest,
@UploadedFiles() files: Array<Express.Multer.File>
) {
this.logger.info('saveCustomertheme' + JSON.stringify({
id,
}));
const logoWhite = files.find(file => file.fieldname === 'logoWhite');
const logoBlack = files.find(file => file.fieldname === 'logoBlack');
this.validFileSize(logoWhite);
this.validFileSize(logoBlack);
this.validMimeType(logoWhite);
this.validMimeType(logoBlack);
try {
const theme = await this.themeService.createThemeByCustomer(id, {
...data,
logoWhite,
logoBlack
});
this.logger.info('saveCustomertheme' + JSON.stringify(theme));
return { theme: theme };
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - saveCustomertheme - Expect CUSTOMER.NOT_FOUND');
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
} else {
this.logger.error('Error - saveCustomertheme Unknown Error:' + err?.message);
return { theme: null };
};
}
}
private validFileSize(file: Express.Multer.File) {
const maxFileSize = 10 * 1024 * 1024; // 10MB
if (file && file.size > maxFileSize) {
throw new HttpException(`O Arquivo ${file.filename} possui mais de 10MB`, HttpStatus.BAD_REQUEST);
}
}
private validMimeType(file: Express.Multer.File) {
const mimeTypesValid = ['image/jpeg', 'image/jpg', 'image/png'];
if (file && !mimeTypesValid.includes(file.mimetype)) {
throw new HttpException(`O Arquivo ${file.fieldname} deve ser jpeg, jpg ou png`, HttpStatus.BAD_REQUEST);
}
}
@Get('/:id/theme')
@ApiOkResponse({ type: CustomerThemeResponse })
async getCustomerTheme(@Param('id') id: string) {
this.logger.info('getCustomerTheme with id' + id);
try {
const data = await this.themeService.getThemeByCustomer(id);
this.logger.info('Success - getCustomerTheme with id'+ id);
return data;
}catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND) {
this.logger.error('Error - getCustomerTheme - Expect CUSTOMER.NOT_FOUND');
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
} else {
this.logger.error('Error - getCustomerTheme Unknown Error:' + err?.message);
return { theme: null };
};
}
}
}
+20
View File
@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { ClientsModule } from '@nestjs/microservices';
import { ThemeController } from './theme.controller';
import { ThemeService } from './theme.service';
import { DucClient } from '../duc/client.config';
const ducClient = new DucClient();
@Module({
imports: [
ClientsModule.register([
ducClient.providerOptions,
]),
],
controllers: [ThemeController],
providers: [ThemeService, DadosferaLogger],
exports: [ThemeService],
})
export class ThemeModule {}
+133
View File
@@ -0,0 +1,133 @@
import {
OnModuleInit,
Inject,
Injectable,
HttpException,
HttpStatus,
InternalServerErrorException,
} from '@nestjs/common';
import { firstValueFrom, lastValueFrom, ReplaySubject } 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 { ThemeProtoService } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/write-service';
import { ThemeRequest } from '@dadosfera/protospack-v2/dist/lib/Duc/interfaces/messages';
import { CustomerThemeRequest, CustomerThemeResponse } from './dtos/customers';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
import { resolve } from 'path';
import { Readable } from 'stream';
type Files = {
logoWhite: Express.Multer.File,
logoBlack: Express.Multer.File,
}
// This function will accept any string, which may result in a bug.
@Injectable()
export class ThemeService implements OnModuleInit {
private themeService: ThemeProtoService;
private logger: DadosferaLogger;
constructor(
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
@Inject(DadosferaLogger)
dadosferaLogger: DadosferaLogger,
) {
this.logger = dadosferaLogger.logger;
}
onModuleInit() {
this.themeService = this.grpcClient.getService<ThemeProtoService>(
ProtoServices.ThemeProtoService,
);
}
async createThemeByCustomer(id: string, theme: CustomerThemeRequest & Files) {
if (!id) {
this.logger.error('Error - saveCustomertheme - not found id:' + id);
throw new HttpException(null, HttpStatus.BAD_REQUEST);
}
const customerThemeRequest$ = new ReplaySubject<ThemeRequest>();
customerThemeRequest$.next({
customerId: id,
displayName: theme.displayName,
backgroundColor: theme.backgroundColor,
textColor: theme.textColor,
isMetadata: true,
filename: '',
chunk: Buffer.alloc(0)
})
if(theme.logoBlack) {
await this.sendFile(theme.logoBlack, customerThemeRequest$);
}
if(theme.logoWhite) {
await this.sendFile(theme.logoWhite, customerThemeRequest$);
}
customerThemeRequest$.complete();
const stream = this.themeService.CustomerCreateTheme(customerThemeRequest$);
return lastValueFrom(stream);
}
async getThemeByCustomer(id: string): Promise<CustomerThemeResponse> {
if (!id) {
this.logger.error('Error - getCustomerTheme - not found id:' + id);
throw new HttpException(null, HttpStatus.BAD_REQUEST);
}
const { theme } = await firstValueFrom(
this.themeService.CustomerGetTheme({
id
}),
);
return {
theme
}
}
private async sendFile(file: Express.Multer.File, stream$: ReplaySubject<ThemeRequest>) {
const bufferStream = new Readable({
highWaterMark: 1024 * 1024, // 1 MB por chunk
read() {}
});
bufferStream.push(file.buffer);
bufferStream.push(null);
return new Promise((resolve, reject) => {
bufferStream.on('data', (chunk) => {
console.log('enviando chunk', file.fieldname)
const mimetype = file.mimetype.split('/')[1]; // example image/jpeg
const filename = file.fieldname.concat(".", mimetype);
stream$.next({
customerId: '',
displayName: '',
backgroundColor: '',
textColor: '',
isMetadata: false,
filename: filename,
chunk: chunk
});
});
bufferStream.on('end', () => {
console.log('terminou de enviar')
resolve(file.filename)
});
bufferStream.on('error', (err) => {
console.error('Erro no stream:', err);
reject(err);
});
});
}
}