@xrystal/core 3.19.8 → 3.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": "Yusuf Yasir KAYGUSUZ",
3
3
  "name": "@xrystal/core",
4
- "version": "3.19.8",
4
+ "version": "3.20.1",
5
5
  "description": "Project core for xrystal",
6
6
  "publishConfig": {
7
7
  "access": "public",
@@ -1,7 +1,8 @@
1
1
  import SystemService from '../system/index';
2
- import { IService } from '../../utils';
2
+ import { IService, ModeEnum } from '../../utils';
3
3
  export interface IConfig {
4
4
  worker: boolean;
5
+ mode: ModeEnum;
5
6
  nodeEnv: string;
6
7
  debug: string;
7
8
  https: string;
@@ -16,7 +17,9 @@ export interface IConfig {
16
17
  projectNameSuffixEnv: string;
17
18
  systemStaticFolderPath: string;
18
19
  systemLoggerLayer: string;
20
+ isKafkaPassive: boolean;
19
21
  kafkaBrokers: string;
22
+ kafkaTopics: string;
20
23
  kafkaLogsTopic: string;
21
24
  baseApiUri: string;
22
25
  port: number;
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { merge } from 'lodash';
3
- import { Constants } from '../../utils';
4
3
  import { pathToFileURL } from 'node:url';
4
+ import { Constants } from '../../utils';
5
5
  export default class ConfigsService {
6
6
  publicFolderName = Constants.publicFolderName;
7
7
  kafkaLogsTopic = Constants.kafkaLogsTopic;
@@ -27,6 +27,7 @@ export default class ConfigsService {
27
27
  const modulePromise = import(fileUrl);
28
28
  this.#config = {
29
29
  worker: process.env.WORKER === 'true' ? true : false || false,
30
+ mode: process.env.MODE,
30
31
  nodeEnv: process.env.NODE_ENV,
31
32
  debug: process.env.SYSTEM_LOGGER_LAYER,
32
33
  https: process.env.HTTPS,
@@ -41,7 +42,9 @@ export default class ConfigsService {
41
42
  projectNameSuffixEnv: process.env.PROJECT_NAME_SUFFIX || '',
42
43
  systemStaticFolderPath: path.resolve(tmp.configs.rootFolderPath, this.publicFolderName),
43
44
  systemLoggerLayer: process.env.SYSTEM_LOGGER_LAYER,
45
+ isKafkaPassive: process.env.IS_KAFKA_PASSIVE === 'true' ? true : false,
44
46
  kafkaBrokers: process.env?.KAFKA_BROKERS,
47
+ kafkaTopics: process.env?.KAFKA_TOPICS,
45
48
  kafkaLogsTopic: this.kafkaLogsTopic,
46
49
  baseApiUri: process.env.HTTPS === 'true' ? process.env.SYSTEM_HTTPS_BASE_API_URI : process.env.SYSTEM_BASE_API_URI,
47
50
  port: Number(process.env.PORT),
@@ -1,6 +1,7 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
2
  import { ProtocolEnum } from '../../utils/index';
3
3
  import LoggerService from '../logger';
4
+ import SystemService from '../system';
4
5
  export declare const controllerContextStorage: AsyncLocalStorage<{
5
6
  protocol?: ProtocolEnum;
6
7
  ctx?: any;
@@ -25,6 +26,7 @@ export declare const getControllerCtx: () => {
25
26
  _isRaw?: boolean;
26
27
  };
27
28
  };
29
+ export declare const protocol: (callback: () => Promise<any>, protocol?: ProtocolEnum) => Promise<any>;
28
30
  export interface CustomRequest {
29
31
  accounts?: any;
30
32
  url: string;
@@ -62,9 +64,11 @@ declare abstract class Controller {
62
64
  protected get res(): CustomResponse;
63
65
  }
64
66
  export declare abstract class ControllerService extends Controller {
65
- load(props?: {
66
- type?: ProtocolEnum | ProtocolEnum[];
67
- }): Promise<void>;
67
+ protected systemService: SystemService;
68
+ constructor({ systemService }: {
69
+ systemService: SystemService;
70
+ });
71
+ load(): Promise<void>;
68
72
  schema({ checks, logic, response }: {
69
73
  checks?: (args: any) => Promise<any>;
70
74
  logic?: (args: any) => Promise<any>;
@@ -3,6 +3,13 @@ import { ProtocolEnum, responseMessageHelper, ResponseSchema, x } from '../../ut
3
3
  import LoggerService from '../logger';
4
4
  export const controllerContextStorage = new AsyncLocalStorage();
5
5
  export const getControllerCtx = () => controllerContextStorage.getStore();
6
+ export const protocol = async (callback, protocol = ProtocolEnum.HTTP) => {
7
+ const store = controllerContextStorage.getStore();
8
+ if (store) {
9
+ store.protocol = protocol;
10
+ }
11
+ return await callback();
12
+ };
6
13
  class Controller {
7
14
  logger = x.get(LoggerService);
8
15
  supportedProtocols = [ProtocolEnum.HTTP, ProtocolEnum.WEBSOCKET];
@@ -72,9 +79,14 @@ class Controller {
72
79
  }
73
80
  }
74
81
  export class ControllerService extends Controller {
75
- async load(props = {}) {
76
- if (props.type)
77
- this.supportedProtocols = Array.isArray(props.type) ? props.type : [props.type];
82
+ systemService;
83
+ constructor({ systemService }) {
84
+ super();
85
+ this.systemService = systemService;
86
+ }
87
+ async load() {
88
+ const protocols = this.systemService.tmp.configs.loaders.controller.protocols;
89
+ this.supportedProtocols = Array.isArray(protocols) ? protocols : [protocols];
78
90
  }
79
91
  async schema({ checks, logic, response }) {
80
92
  try {
@@ -1,11 +1,14 @@
1
+ import ConfigsService from "../configs";
1
2
  import { IService } from "../../utils";
3
+ import SystemService from "../system";
2
4
  export default class LocalizationsService implements IService {
5
+ #private;
3
6
  _instance: any;
4
- load: ({ loadPath, fallbackLang, preloadLang, }: {
5
- loadPath: string;
6
- fallbackLang: string;
7
- preloadLang: string;
8
- }) => any;
7
+ constructor({ systemService, configsService, }: {
8
+ systemService: SystemService;
9
+ configsService: ConfigsService;
10
+ });
11
+ load: ({}: {}) => any;
9
12
  i18next: ({ loadPath, fallbackLang, preloadLang, }: {
10
13
  loadPath?: string;
11
14
  fallbackLang?: string;
@@ -3,11 +3,18 @@ import Backend from 'i18next-fs-backend';
3
3
  import * as middleware from 'i18next-http-middleware';
4
4
  export default class LocalizationsService {
5
5
  _instance = null;
6
- load = ({ loadPath, fallbackLang, preloadLang, }) => {
6
+ #systemService;
7
+ #configsService;
8
+ constructor({ systemService, configsService, }) {
9
+ this.#systemService = systemService;
10
+ this.#configsService = configsService;
11
+ }
12
+ load = ({}) => {
13
+ const localization = this.#systemService.tmp.configs.loaders.localization;
7
14
  this.i18next({
8
- loadPath,
9
- fallbackLang,
10
- preloadLang,
15
+ loadPath: localization.loadPath,
16
+ fallbackLang: localization.fallbackLang,
17
+ preloadLang: localization.preloadLang,
11
18
  });
12
19
  };
13
20
  i18next = ({ loadPath, fallbackLang, preloadLang, }) => {
@@ -1,7 +1,9 @@
1
1
  import winston from "winston";
2
2
  import "winston-daily-rotate-file";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
+ import ConfigsService from "../configs";
4
5
  import { LoggerLayerEnum, IService } from "../../utils";
6
+ import SystemService from "../system";
5
7
  export interface ICustomLogger extends winston.Logger {
6
8
  critical: winston.LeveledLogMethod;
7
9
  http: winston.LeveledLogMethod;
@@ -20,10 +22,11 @@ export default class LoggerService implements IService {
20
22
  private kafkaLogsTopic;
21
23
  private isKafkaReady;
22
24
  winston: ICustomLogger;
23
- constructor({ configsService }: {
24
- configsService: any;
25
+ constructor({ systemService, configsService }: {
26
+ systemService: SystemService;
27
+ configsService: ConfigsService;
25
28
  });
26
- load: (config: Record<string, any>) => Promise<void>;
29
+ load: ({}: {}) => Promise<void>;
27
30
  winstonLoader: ({ loadPath, loggerLevel }: {
28
31
  loadPath: string;
29
32
  loggerLevel: string;
@@ -38,23 +38,27 @@ export default class LoggerService {
38
38
  kafkaLogsTopic = "";
39
39
  isKafkaReady = false;
40
40
  winston;
41
+ #systemService;
41
42
  #configsService;
42
- constructor({ configsService }) {
43
+ constructor({ systemService, configsService }) {
44
+ this.#systemService = systemService;
43
45
  this.#configsService = configsService;
44
46
  this.kafkaLogsTopic = this.#configsService?.all.kafkaLogsTopic;
45
47
  winston.addColors(customColors);
46
48
  this.winston = winston.createLogger({
47
- level: this.#configsService.all.systemLoggerLayer,
49
+ level: this.#configsService.all.systemLoggerLayer || 'info',
48
50
  levels: customLevels,
49
51
  format: this.getConsoleFormat(),
50
52
  transports: [new winston.transports.Console()]
51
53
  });
52
54
  }
53
- load = async (config) => {
54
- this.serviceName = config?.serviceName;
55
- const rawBrokers = this.#configsService.all.kafkaBrokers;
56
- const brokers = rawBrokers ? String(rawBrokers).split(",").map((b) => b.trim()) : [];
57
- if (brokers.length > 0) {
55
+ load = async ({}) => {
56
+ const loggersConfigs = this.#systemService.tmp.configs.loaders.loggers;
57
+ this.serviceName = this.#systemService.tmp.configs?.serviceName;
58
+ const { kafkaBrokers, isKafkaPassive } = this.#configsService.all;
59
+ const brokers = kafkaBrokers ? String(kafkaBrokers).split(",").map((b) => b.trim()) : [];
60
+ const isKafkaEnabled = isKafkaPassive === false && brokers.length > 0;
61
+ if (isKafkaEnabled) {
58
62
  const kafka = new Kafka({
59
63
  clientId: this.serviceName,
60
64
  brokers: brokers,
@@ -73,8 +77,8 @@ export default class LoggerService {
73
77
  }
74
78
  }
75
79
  this.winstonLoader({
76
- loadPath: config?.loadPath,
77
- loggerLevel: config?.loggerLevel
80
+ loadPath: path.join(this.#configsService.all.rootFolderPath, loggersConfigs?.loadPath),
81
+ loggerLevel: loggersConfigs?.loggerLevel
78
82
  });
79
83
  };
80
84
  winstonLoader = ({ loadPath, loggerLevel }) => {
@@ -100,7 +104,7 @@ export default class LoggerService {
100
104
  maxFiles: "14d"
101
105
  })
102
106
  ];
103
- if (this.kafkaProducer) {
107
+ if (this.isKafkaReady && this.kafkaProducer) {
104
108
  transports.push(new KafkaTransport({ level: loggerLevel }, this));
105
109
  }
106
110
  this.winston = winston.createLogger({
@@ -123,11 +127,6 @@ export default class LoggerService {
123
127
  if (store && store.has("correlationId")) {
124
128
  info.id = store.get("correlationId");
125
129
  }
126
- Object.keys(info).forEach(key => {
127
- if (info[key] instanceof Headers) {
128
- info[key] = Object.fromEntries(info[key].entries());
129
- }
130
- });
131
130
  return info;
132
131
  });
133
132
  getConsoleFormat() {
@@ -1,10 +1,14 @@
1
1
  import { IService } from "../../utils";
2
2
  export default class SystemService implements IService {
3
- _tmp: Record<string, any>;
3
+ private _tmp;
4
4
  cwd: string;
5
- load: ({ tmp, }: {
5
+ core: {
6
+ _: any;
7
+ };
8
+ load: ({}: {
6
9
  tmp: any;
7
10
  }) => Promise<void>;
11
+ private initializeKafkaInfrastructure;
8
12
  private _systemLoader;
9
13
  get tmp(): Record<string, any>;
10
14
  }
@@ -1,14 +1,65 @@
1
+ import { Kafka, logLevel } from "kafkajs";
2
+ import { Constants, getTmp } from "../../utils";
1
3
  export default class SystemService {
2
4
  _tmp = {};
3
5
  cwd = process.cwd();
4
- load = async ({ tmp, }) => {
5
- this._tmp = tmp;
6
+ core = getTmp();
7
+ load = async ({}) => {
8
+ this._tmp = this.core._;
9
+ await this.initializeKafkaInfrastructure();
6
10
  await this._systemLoader({});
7
11
  };
12
+ initializeKafkaInfrastructure = async () => {
13
+ const { kafkaBrokers, kafkaTopics, isKafkaPassive, serviceName } = {
14
+ isKafkaPassive: process.env.IS_KAFKA_PASSIVE === 'true' ? true : false,
15
+ kafkaBrokers: process.env?.KAFKA_BROKERS,
16
+ kafkaTopics: [
17
+ ...new Set([
18
+ Constants.kafkaLogsTopic,
19
+ ...(process.env.KAFKA_TOPICS?.split(',').map(t => t.trim()).filter(Boolean) || [])
20
+ ])
21
+ ],
22
+ serviceName: this._tmp.configs.service
23
+ };
24
+ if (isKafkaPassive === true || !kafkaBrokers)
25
+ return;
26
+ const brokers = String(kafkaBrokers).split(",").map(b => b.trim());
27
+ const topicsToCreate = Array.isArray(kafkaTopics) ? kafkaTopics : [];
28
+ if (topicsToCreate.length === 0)
29
+ return;
30
+ const kafka = new Kafka({
31
+ clientId: `${serviceName}`,
32
+ brokers,
33
+ logLevel: logLevel.NOTHING
34
+ });
35
+ const admin = kafka.admin();
36
+ try {
37
+ await admin.connect();
38
+ const existingTopics = await admin.listTopics();
39
+ const newTopics = topicsToCreate
40
+ .filter(topic => !existingTopics.includes(topic))
41
+ .map(topic => ({
42
+ topic,
43
+ numPartitions: 1,
44
+ replicationFactor: 1
45
+ }));
46
+ if (newTopics.length > 0) {
47
+ await admin.createTopics({
48
+ waitForLeaders: true,
49
+ topics: newTopics
50
+ });
51
+ }
52
+ }
53
+ catch (error) {
54
+ // => Error
55
+ }
56
+ finally {
57
+ await admin.disconnect();
58
+ }
59
+ };
8
60
  _systemLoader = async ({}) => {
9
- //console.log(this._tmp)
61
+ return;
10
62
  };
11
- // => getters
12
63
  get tmp() {
13
64
  return this._tmp;
14
65
  }
@@ -1,6 +1,3 @@
1
- export declare const core: {
2
- _: any;
3
- };
4
1
  export declare const coreInit: (params: {
5
2
  locales?: Record<string, any>;
6
3
  }) => Promise<void>;
@@ -1,9 +1,8 @@
1
1
  import path from 'path';
2
2
  import { SystemService, ConfigsService, LoggerService, EventsService, LocalizationsService, ClientsService, ControllerService } from '../loader';
3
- import { Constants, x, getTmp, } from '../utils';
3
+ import { Constants, x, } from '../utils';
4
4
  //
5
5
  let coreHasRun = false;
6
- export const core = getTmp();
7
6
  export const coreInit = async (params) => {
8
7
  const { locales } = params;
9
8
  if (locales)
@@ -13,8 +12,6 @@ const coreLoader = async ({}) => {
13
12
  if (coreHasRun)
14
13
  return {};
15
14
  try {
16
- const { configs } = core._;
17
- const rootFolderPath = configs.rootFolderPath;
18
15
  await (await x.load([
19
16
  path.join(__dirname, '..', 'loader', '**/*.{ts,js}'),
20
17
  ], {
@@ -23,23 +20,15 @@ const coreLoader = async ({}) => {
23
20
  .initialize([
24
21
  {
25
22
  service: SystemService,
26
- props: {
27
- tmp: core._
28
- }
23
+ props: {}
29
24
  },
30
25
  {
31
26
  service: ConfigsService,
32
- props: {
33
- ...(configs.loaders.configs.globalEnvFileName && { globalEnvFileName: configs.loaders.configs.globalEnvFileName })
34
- }
27
+ props: {}
35
28
  },
36
29
  {
37
30
  service: LoggerService,
38
- props: {
39
- loadPath: path.join(rootFolderPath, configs.loaders.loggers.loadPath),
40
- loggerLevel: configs.loaders.loggers.logLevel,
41
- serviceName: configs.service,
42
- }
31
+ props: {}
43
32
  },
44
33
  {
45
34
  service: EventsService,
@@ -47,17 +36,11 @@ const coreLoader = async ({}) => {
47
36
  },
48
37
  {
49
38
  service: LocalizationsService,
50
- props: {
51
- loadPath: path.join(rootFolderPath, configs.loaders.localization.loadPath),
52
- fallbackLang: configs.loaders.localization.fallbackLang,
53
- preloadLang: configs.loaders.localization.preloadLangs
54
- }
39
+ props: {}
55
40
  },
56
41
  {
57
42
  service: ControllerService,
58
- props: {
59
- type: configs.loaders.controller.protocols,
60
- }
43
+ props: {}
61
44
  },
62
45
  {
63
46
  service: ClientsService,
@@ -76,15 +76,9 @@ export declare enum TimezoneEnum {
76
76
  EtcUTC = "Etc/UTC",
77
77
  EtcGMT = "Etc/GMT"
78
78
  }
79
- export declare enum WsActionsEnum {
80
- REGISTER = "register",
81
- JOIN = "join",
82
- MESSAGE = "message"
83
- }
84
- export declare enum WsKeyEnum {
85
- BUS = "WS_BUS",
86
- USER = "ws:user",
87
- ROOM = "ws:room"
79
+ export declare enum ModeEnum {
80
+ MONOLITIC = "monolitic",
81
+ MICRO_SERVICE = "micro_service"
88
82
  }
89
83
  export declare enum TokensEnum {
90
84
  INTERNAL_API_TOKEN = "internal_api_key",
@@ -90,18 +90,11 @@ export var TimezoneEnum;
90
90
  TimezoneEnum["EtcUTC"] = "Etc/UTC";
91
91
  TimezoneEnum["EtcGMT"] = "Etc/GMT";
92
92
  })(TimezoneEnum || (TimezoneEnum = {}));
93
- export var WsActionsEnum;
94
- (function (WsActionsEnum) {
95
- WsActionsEnum["REGISTER"] = "register";
96
- WsActionsEnum["JOIN"] = "join";
97
- WsActionsEnum["MESSAGE"] = "message";
98
- })(WsActionsEnum || (WsActionsEnum = {}));
99
- export var WsKeyEnum;
100
- (function (WsKeyEnum) {
101
- WsKeyEnum["BUS"] = "WS_BUS";
102
- WsKeyEnum["USER"] = "ws:user";
103
- WsKeyEnum["ROOM"] = "ws:room";
104
- })(WsKeyEnum || (WsKeyEnum = {}));
93
+ export var ModeEnum;
94
+ (function (ModeEnum) {
95
+ ModeEnum["MONOLITIC"] = "monolitic";
96
+ ModeEnum["MICRO_SERVICE"] = "micro_service";
97
+ })(ModeEnum || (ModeEnum = {}));
105
98
  export var TokensEnum;
106
99
  (function (TokensEnum) {
107
100
  TokensEnum["INTERNAL_API_TOKEN"] = "internal_api_key";
package/x/tmp.yml CHANGED
@@ -21,7 +21,7 @@ configs:
21
21
  - tr
22
22
 
23
23
  controller:
24
- protocol: http
24
+ protocols: http
25
25
 
26
26
  end:
27
27
  version: 1