jhipster / generator-jhipster-nodejs

A NodeJS blueprint that creates the backend using NestJS
https://www.npmjs.com/package/generator-jhipster-nodejs
Apache License 2.0
257 stars 81 forks source link

JHipster Registry doesn't recognize microservices or gateway #265

Open keyz231 opened 2 years ago

keyz231 commented 2 years ago

I have installed a microservice using the guide:

npm install -g generator-jhipster-nodejs

and my microservice works ( for example I've tried to go to localhost:8081/api/users)

I have downloaded the jhipster-registry from github and start with mvwn

Now my registry recognize only 👍 image

But nothing about the microservice.

About microservice:

.yo-rc.json

  "generator-jhipster": {
    "blueprints": [
      {
        "name": "generator-jhipster-nodejs",
        "version": "2.0.0"
      }
    ],
    "otherModules": [
      {
        "name": "generator-jhipster-nodejs",
        "version": "2.0.0"
      }
    ],
    "applicationType": "microservice",
    "baseName": "microservice",
    "jhipsterVersion": "7.0.1",
    "skipClient": true,
    "skipUserManagement": true,
    "skipServer": false,
    "skipCheckLengthOfIdentifier": false,
    "skipFakeData": false,
    "jhiPrefix": "jhi",
    "entitySuffix": "",
    "dtoSuffix": "DTO",
    "testFrameworks": [],
    "pages": [],
    "creationTimestamp": 1645529869157,
    "serverPort": "8081",
    "packageName": "com.jhipster.node",
    "databaseType": "sql",
    "devDatabaseType": "sqlite",
    "prodDatabaseType": "oracle",
    "authenticationType": "jwt",
    "enableTranslation": false,
    "nativeLanguage": "it",
    "packageFolder": "com/jhipster/node",
    "jwtSecretKey": "Yjg1ZGExYWU2YjhiNjZhNDUwMTc5ZDUzNzhlMGFkOGY1MWUwODdmYWY0OWQ1NTVhZTc3N2U4NTU5N2M4MDg5Njg1YmY4NDQzMjc1NjIxYzg3ZDc3MDllMDdiZGFmNDE4ODBkYTVlMWIxZWFhYjdkNjBkYjNmOTc4NTczYzEyNWE=",
    "serviceDiscoveryType": false,
    "websocket": false,
    "searchEngine": false,
    "messageBroker": false,
    "cacheProvider": "ehcache",
    "buildTool": "maven",
    "enableHibernateCache": true,
    "reactive": false,
    "languages": ["en", "fr"]
  }
}

server/src/client/config/application.yml

# ===================================================================
# Configuration.
#
# This configuration will be overridden by the BACKEND_ENV you use,
# for example application-dev.yml if you use the "dev" profile.
#
# More information on profiles: https://www.jhipster.tech/profiles/
# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
# ===================================================================

# ===================================================================
# Standard app properties.
# ===================================================================

eureka:
  client:
    enabled: false
    healthcheck:
      enabled: true
    fetch-registry: true
    register-with-eureka: true
    instance-info-replication-interval-seconds: 10
    registry-fetch-interval-seconds: 10
  instance:
    appname: microservice
    instanceId: microservice:${random.value}
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 10
    status-page-url-path: ${management.endpoints.web.base-path}/info
    health-check-url-path: ${management.endpoints.web.base-path}/health
    metadata-map:
      zone: primary # This is needed for the load balancer
      version: #project.version#
      git-version: ${git.commit.id.describe:}
      git-commit: ${git.commit.id.abbrev:}
      git-branch: ${git.branch:}

# ===================================================================
# JHipster specific properties
#
# Full reference is available at: https://www.jhipster.tech/common-application-properties/
# ===================================================================

jhipster:
  clientApp:
    name: 'microservice'
  registry:
    password: admin
  security:
    authentication:
      jwt:
        # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one)
        base64-secret: NDhjNjk1NTM3NWRlNjc1NDMwZjllNWFiMmVlYjQ4NzViYzY4MmY5ZWY2MzZhMzNiMTYxYmNlYjJkMWYwNDk0NDBlNDYwZThjMmFmNzAyNTQyOWYxMDhkM2QxYTQ3ZDFjM2I5YWU4YWVjOGRhNDc3MWE5OTExMzUyMjI3MDlmZWM=
        # Token is valid 24 hours
        token-validity-in-seconds: 86400
        token-validity-in-seconds-for-remember-me: 2592000
  mail:
    from: microservice@localhost
  swagger:
    default-include-pattern: /api/.*
    title: microservice API
    description: microservice API documentation
    version: 0.0.1
    terms-of-service-url:
    contact-name:
    contact-url:
    contact-email:
    license:
    license-url:
    path: /api/v2/api-docs

cloud:
  config:
    uri: http://admin:${jhipster.registry.password}@localhost:8761/config
    # name of the config server's property source (file.yml) that we want to use
    name: microservice
    profile: prod # profile(s) of the property source
    label: master # toggle to switch to a different version of the configuration as stored in git
    # it can be set to any label, branch or commit of the configuration source Git repository
# ===================================================================
# Application specific properties
# Add your own application properties here, see the ApplicationProperties class
# to have type-safe configuration, like in the JHipsterProperties above
#
# More documentation is available at:
# https://www.jhipster.tech/common-application-properties/
# ===================================================================

# application:

server/src/client/config/application-dev.yml

eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/

server:
  port: 8081

  mail:
    host: localhost
    port: 25
    username:
    password:

jhipster:
  registry:
      password: admin
  mail: # specific JHipster mail property, for standard properties see MailProperties
    from: microservice@localhost
    base-url: http://127.0.0.1:8082

I have also tried with a gateway, but the result is the same. How can I set my microservice to the registry? Thank you

ghost commented 2 years ago

Hi @keyz231, thanks for the issue. Could you add your .yo-rc.json file? Thanks

keyz231 commented 2 years ago

Thank you for answer, I have insert .yo-rc.json at the beginning, do you refer to it ?

ghost commented 2 years ago

Hi @keyz231, view the issue #103 . Thanks for the issue.

keyz231 commented 2 years ago

Thank you for your answer, So I can't use registry with microservices? Anyway before opening the post I had given a good read to all the questions, but the difference with the one suggested by you is that my microservice can't even register (as if it wasn't even activated)

ghost commented 2 years ago

The feature has been implemented. See the last linked issue in the comments. For example:https://github.com/jhipster/generator-jhipster-nodejs/issues/103#issuecomment-704199573

Yomesh1995 commented 2 years ago

@amanganiello90 As you said this feature has been implemented, but still microservices or gateway metadata info & git commit info are not visible like jhipster-registry or any other jhipster(JAVA) microservice. Screenshot from 2022-08-06 23-49-13

Mircoservice status Screenshot from 2022-08-06 23-50-35

.yo-rc.json

{
  "generator-jhipster": {
    "blueprints": [
      {
        "name": "generator-jhipster-nodejs",
        "version": "2.0.0"
      }
    ],
    "otherModules": [
      {
        "name": "generator-jhipster-nodejs",
        "version": "2.0.0"
      }
    ],
    "applicationType": "microservice",
    "baseName": "result",
    "jhipsterVersion": "7.0.1",
    "skipClient": true,
    "skipUserManagement": true,
    "skipServer": false,
    "skipCheckLengthOfIdentifier": false,
    "skipFakeData": true,
    "jhiPrefix": "jhi",
    "entitySuffix": "",
    "dtoSuffix": "DTO",
    "testFrameworks": [],
    "pages": [],
    "creationTimestamp": 1659788990090,
    "serverPort": "8082",
    "packageName": "com.jhipster.node",
    "databaseType": "sql",
    "devDatabaseType": "sqlite",
    "prodDatabaseType": "mysql",
    "authenticationType": "jwt",
    "enableTranslation": true,
    "nativeLanguage": "en",
    "packageFolder": "com/jhipster/node",
    "jwtSecretKey": "ZjRlMDI3OWNhYWE4MjRkYWZlODc2YTg4NTJiMGVjOGM2OTdkOTI3YWNmMDA2ODVmMGMxNzc3OTExNzA1YmY1N2Y4ZjFkZTE3MGI3OWZiNjg0OTE3Mjk0ZjQ4YjZhODYzZTU5YWNjMmQyNjNkNWQ3MDBlMTJiN2QwNmRiNTE3NjM=",
    "serviceDiscoveryType": true,
    "websocket": false,
    "searchEngine": false,
    "messageBroker": false,
    "cacheProvider": "ehcache",
    "buildTool": "maven",
    "enableHibernateCache": true,
    "reactive": false,
    "languages": ["en", "fr"],
    "clientFramework": "angularX",
    "clientPackageManager": "npm",
    "clientTheme": "none",
    "clientThemeVariant": "",
    "withAdminUi": true,
    "entities": []
  }
}

application.yml

eureka:
  client:
    enabled: true
    healthcheck:
      enabled: true
    fetch-registry: true
    register-with-eureka: true
    instance-info-replication-interval-seconds: 10
    registry-fetch-interval-seconds: 10
  instance:
    appname: result
    instanceId: result:${random.value}
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 10
    status-page-url-path: ${management.endpoints.web.base-path}/info
    health-check-url-path: ${management.endpoints.web.base-path}/health
    metadata-map:
      zone: primary # This is needed for the load balancer
      version: #project.version#
      git-version: ${git.commit.id.describe:}
      git-commit: ${git.commit.id.abbrev:}
      git-branch: ${git.branch:}

jhipster:
  clientApp:
    name: 'result'
  registry:
    password: admin
  security:
    authentication:
      jwt:
        # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one)
        base64-secret: NDhjNjk1NTM3NWRlNjc1NDMwZjllNWFiMmVlYjQ4NzViYzY4MmY5ZWY2MzZhMzNiMTYxYmNlYjJkMWYwNDk0NDBlNDYwZThjMmFmNzAyNTQyOWYxMDhkM2QxYTQ3ZDFjM2I5YWU4YWVjOGRhNDc3MWE5OTExMzUyMjI3MDlmZWM=
        # Token is valid 24 hours
        token-validity-in-seconds: 86400
        token-validity-in-seconds-for-remember-me: 2592000
  mail:
    from: result@localhost
  swagger:
    default-include-pattern: /api/.*
    title: result API
    description: result API documentation
    version: 0.0.1
    terms-of-service-url:
    contact-name:
    contact-url:
    contact-email:
    license:
    license-url:
    path: /api/v2/api-docs

cloud:
  config:
    uri: http://admin:${jhipster.registry.password}@localhost:8761/config
    # name of the config server's property source (file.yml) that we want to use
    name: result
    profile: prod # profile(s) of the property source
    label: master # toggle to switch to a different version of the configuration as stored in git

config.ts

import yaml from 'js-yaml';
import * as fs from 'fs';
import * as path from 'path';
import { Logger } from '@nestjs/common';

const logger = new Logger('Config');

export class Config {
    debugLogging = 'debug';
    'server.port' = '8082';
    'jhipster.clientApp.name' = 'result';
    'jhipster.registry.password' = 'admin';
    'jhipster.security.authentication.jwt.base64-secret' = '';
    'jhipster.security.authentication.jwt.token-validity-in-seconds' = 86400;
    'jhipster.security.authentication.jwt.token-validity-in-seconds-for-remember-me' = 2592000;
    'jhipster.security.authentication.jwt.hash-salt-or-rounds' = 10;
    'jhipster.mail.base-url' = 'http://127.0.0.1:${server.port}';
    'jhipster.mail.from' = 'result@localhost';
    'jhipster.swagger.default-include-pattern' = '/api/.*';
    'jhipster.swagger.title' = 'result API';
    'jhipster.swagger.description' = 'result API documentation';
    'jhipster.swagger.version' = '0.0.1';
    'jhipster.swagger.path' = '/api/v2/api-docs';
    'eureka.client.enabled' = true;
    'eureka.client.healthcheck.enabled' = true;
    'eureka.client.fetch-registry' = true;
    'eureka.client.register-with-eureka' = true;
    'eureka.client.instance-info-replication-interval-seconds' = 10;
    'eureka.client.registry-fetch-interval-seconds' = 10;
    'eureka.instance.appname' = 'result';
    'eureka.instance.instanceId' = 'result:${random.value}';
    'eureka.instance.lease-renewal-interval-in-seconds' = 5;
    'eureka.instance.lease-expiration-duration-in-seconds' = 10;
    'eureka.instance.status-page-url-path' = '${management.endpoints.web.base-path}/info';
    'eureka.instance.health-check-url-path' = '${management.endpoints.web.base-path}/health';
    'eureka.instance.metadata-map.zone' = 'primary';
    // 'eureka.instance.metadata-map.profile' = '${profiles.active}';
    'eureka.instance.metadata-map.git-version' = '${git.commit.id.describe:}';
    'eureka.instance.metadata-map.git-commit' = '${git.commit.id.abbrev:}';
    'eureka.instance.metadata-map.git-branch' = '${git.branch:}';
    'eureka.instance.prefer-ip-address' = true;
    'eureka.client.service-url.defaultZone' = 'http://admin:${jhipster.registry.password}@localhost:8761/eureka/';
    'cloud.config.uri' = 'http://admin:${jhipster.registry.password}@localhost:8761/config';
    'cloud.config.name' = 'result';
    'cloud.config.profile' = 'prod';
    'cloud.config.label' = 'master';

    constructor(properties) {
        this.addAll(properties);
    }

    public get(key: string): any {
        return this[key];
    }

    public addAll(properties): any {
        properties = objectToArray(properties);
        for (const property in properties) {
            if (properties.hasOwnProperty(property)) {
                this[property] = properties[property];
            }
        }
        this.postProcess();
    }

    public postProcess(): any {
        const variables = { ...this, ...process.env };
        for (const property in this) {
            if (this.hasOwnProperty(property)) {
                const value = this[property];
                const processedValue = this.processTemplate(value, variables);
                this[property] = processedValue;
            }
        }
    }

    private processTemplate(template, variables): any {
        // console.log(template);
        if (typeof template === 'string') {
            return template.replace(
                new RegExp('\\${[^{]+}', 'g'),
                name => variables[name.substring(2, name.length - 1)],
            );
        }
        return template;
    }
}

const yamlConfigPath = path.join(__dirname, 'config', 'application.yml');
const envYamlConfigPath = path.join(__dirname, 'config', `application-${process.env.BACKEND_ENV}.yml`);

const yamlConfig = yaml.load(fs.readFileSync(yamlConfigPath, 'utf8'));
logger.log(`Actual process.env.BACKEND_ENV value: ${process.env.BACKEND_ENV}`);
logger.log('Standard allowed values are: dev, test or prod');
logger.log(
    'if you run with a non standard BACKEND_ENV value, remember to add your application-{process.env.BACKEND_ENV}.yml file',
);
if (!fs.existsSync(envYamlConfigPath)) {
    logger.error(
        'An application-{process.env.BACKEND_ENV}.yml file with your process.env.BACKEND_ENV value does not exist under config folder!',
    );
}
const envYamlConfig = yaml.load(fs.readFileSync(envYamlConfigPath, 'utf8'));

const config = new Config({ ...objectToArray(yamlConfig), ...objectToArray(envYamlConfig), ipAddress: ipAddress() });

export { config };

function objectToArray(source, currentKey?, target?): any {
    target = target || {};
    for (const property in source) {
        if (source.hasOwnProperty(property)) {
            const newKey = currentKey ? currentKey + '.' + property : property;
            const newVal = source[property];

            if (typeof newVal === 'object') {
                objectToArray(newVal, newKey, target);
            } else {
                target[newKey] = newVal;
            }
        }
    }
    return target;
}

function ipAddress(): any {
    const interfaces = require('os').networkInterfaces();
    for (const dev in interfaces) {
        if (interfaces.hasOwnProperty(dev)) {
            const iface = interfaces[dev];
            for (const alias of iface) {
                if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
                    return alias.address;
                }
            }
        }
    }

    return null;
}

main.ts

require('dotenv').config({ path: '.env' });
import { NestFactory } from '@nestjs/core';
import cloudConfigClient from 'cloud-config-client';
import { AppModule } from './app.module';
import { setupSwagger } from './swagger';
import { config } from './config';
import { Logger, ValidationPipe, BadRequestException } from '@nestjs/common';
const logger: Logger = new Logger('Main');
const port = process.env.NODE_SERVER_PORT || config.get('server.port');
const useJHipsterRegistry = config.get('eureka.client.enabled');

async function bootstrap(): Promise<void> {
    loadCloudConfig();
    registerAsEurekaService();

    const appOptions = { cors: true };
    const app = await NestFactory.create(AppModule, appOptions);
    app.useGlobalPipes(
        new ValidationPipe({
            exceptionFactory: (): BadRequestException => new BadRequestException('Validation error'),
        }),
    );

    logger.log('The client is not been generated');
    setupSwagger(app);

    await app.listen(port);
    logger.log(`Application listening on port ${port}`);
}

async function loadCloudConfig(): Promise<void> {
    if (useJHipsterRegistry) {
        const endpoint = config.get('cloud.config.uri') || 'http://admin:admin@localhost:8761/config';
        logger.log(`Loading cloud config from ${endpoint}`);

        const cloudConfig = await cloudConfigClient.load({
            context: process.env,
            endpoint,
            name: config.get('cloud.config.name'),
            profiles: config.get('cloud.config.profile') || ['prod'],
            // auth: {
            //   user: config.get('jhipster.registry.username') || 'admin',
            //   pass: config.get('jhipster.registry.password') || 'admin'
            // }
        });
        config.addAll(cloudConfig.properties);
    }
}

function registerAsEurekaService(): void {
    if (useJHipsterRegistry) {
        logger.log(`Registering with eureka ${config.get('cloud.config.uri')}`);
        const Eureka = require('eureka-js-client').Eureka;
        const eurekaUrl = require('url').parse(config.get('cloud.config.uri'));
        const client = new Eureka({
            instance: {
                app: config.get('eureka.instance.appname'),
                instanceId: config.get('eureka.instance.instanceId'),
                hostName: config.get('ipAddress') || 'localhost',
                ipAddr: config.get('ipAddress') || '127.0.0.1',
                status: 'UP',
                port: {
                    $: port,
                    '@enabled': 'true',
                },
                vipAddress: config.get('ipAddress') || 'localhost',
                homePageUrl: `http://${config.get('ipAddress')}:${port}/`,
                dataCenterInfo: {
                    '@class': 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo',
                    name: 'MyOwn',
                },
            },
            eureka: {
                // eureka server host / port
                host: eurekaUrl.hostname || '127.0.0.1',
                port: eurekaUrl.port || 8761,
                servicePath: '/eureka/apps',
            },
            requestMiddleware: (requestOpts, done): any => {
                requestOpts.auth = {
                    user: config.get('jhipster.registry.username') || 'admin',
                    password: config.get('jhipster.registry.password') || 'admin',
                };
                done(requestOpts);
            },
        });
        client.logger.level('debug');
        client.start(error => logger.log(error || 'Eureka registration complete'));
    }
}

bootstrap();