@canmingir/link-express 1.7.3 → 1.7.6

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/bin/cli.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canmingir/link-express",
3
- "version": "1.7.3",
3
+ "version": "1.7.6",
4
4
  "description": "",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
@@ -4,6 +4,7 @@ import { EventMetrics, PushgatewayConfig } from "./metrics";
4
4
  import { KafkaAdapter } from "./adapters/KafkaAdapter";
5
5
  import { SocketAdapter } from "./adapters/SocketAdapter";
6
6
  import { TxEventQAdapter } from "./adapters/TxEventQAdapter";
7
+ import { logEvent } from "../eventLogger";
7
8
 
8
9
  const TOPICS = [
9
10
  "KNOWLEDGE_CREATED",
@@ -90,6 +91,9 @@ export class EventManager {
90
91
  const type = args.slice(0, -1) as string[];
91
92
  const mergedType = type.join("_");
92
93
  this.validateEventType(mergedType);
94
+
95
+ logEvent("publish", mergedType, payload);
96
+
93
97
  const payloadSize = JSON.stringify(payload).length;
94
98
  const endTimer = this.metrics.recordPublish(mergedType, payloadSize);
95
99
  try {
@@ -107,6 +111,8 @@ export class EventManager {
107
111
  type: string,
108
112
  callback: Callback<T>
109
113
  ): Promise<() => void> {
114
+ logEvent("subscribe", type);
115
+
110
116
  if (!this.callbacks.has(type)) {
111
117
  this.callbacks.set(type, new Set());
112
118
  }
@@ -0,0 +1,74 @@
1
+ import { ecsFormat } from "@elastic/ecs-pino-format";
2
+ import pino from "pino";
3
+ import pinoElastic from "pino-elasticsearch";
4
+ import { getConfig } from "../config";
5
+
6
+ let logger: pino.Logger | null = null;
7
+ let isElasticsearchConfigured = false;
8
+
9
+ function getLogger(): pino.Logger | null {
10
+ const { logger: loggerConfig, project } = getConfig();
11
+
12
+ if (!loggerConfig || !project) {
13
+ isElasticsearchConfigured = false;
14
+ return null;
15
+ }
16
+
17
+ if (!logger || !isElasticsearchConfigured) {
18
+ const streams: pino.StreamEntry[] = [{ stream: process.stdout }];
19
+
20
+ const eventsIndex = `${loggerConfig.elasticsearch.index}-events`;
21
+
22
+ const streamToElastic = pinoElastic({
23
+ index: eventsIndex,
24
+ node: loggerConfig.elasticsearch.node,
25
+ esVersion: loggerConfig.elasticsearch.esVersion || 8,
26
+ flushBytes: loggerConfig.elasticsearch.flushBytes || 1000,
27
+ } as Parameters<typeof pinoElastic>[0]);
28
+
29
+ streamToElastic.on("error", (err: Error) => {
30
+ console.error("[EventLogger] Elasticsearch error:", err.message);
31
+ });
32
+
33
+ streamToElastic.on("insertError", (err: Error) => {
34
+ console.error("[EventLogger] Elasticsearch insert error:", err);
35
+ });
36
+
37
+ streams.push({ stream: streamToElastic });
38
+
39
+ logger = pino(
40
+ {
41
+ ...ecsFormat(),
42
+ base: {
43
+ service: {
44
+ name: project.name,
45
+ version: project.version,
46
+ },
47
+ },
48
+ },
49
+ pino.multistream(streams)
50
+ );
51
+
52
+ isElasticsearchConfigured = true;
53
+ }
54
+
55
+ return logger;
56
+ }
57
+
58
+ export function logEvent(
59
+ action: "publish" | "subscribe",
60
+ topic: string,
61
+ payload?: any
62
+ ) {
63
+ const log = getLogger();
64
+
65
+ if (log) {
66
+ log.info({
67
+ action,
68
+ topic,
69
+ event_payload: payload ? JSON.stringify(payload) : undefined,
70
+ });
71
+ } else {
72
+ console.log(`[Event] ${action}:`, topic, payload || "");
73
+ }
74
+ }
@@ -1,5 +1,6 @@
1
1
  import client from "prom-client";
2
2
  import { v4 as uuid } from "uuid";
3
+ import { logEvent } from "../eventLogger";
3
4
 
4
5
  const subscriptions = {};
5
6
  const messages = new Map();
@@ -102,7 +103,7 @@ const subscribe = (...args) => {
102
103
  const type = args.join(".");
103
104
  const id = uuid();
104
105
 
105
- console.debug("node-event", "subscribe", type, id);
106
+ logEvent("subscribe", type);
106
107
 
107
108
  if (type === "__proto__" || type === "constructor" || type === "prototype") {
108
109
  throw new Error("Invalid subscription type");
@@ -116,7 +117,6 @@ const subscribe = (...args) => {
116
117
  type,
117
118
  callback,
118
119
  unsubscribe: () => {
119
- console.debug("node-event", "unsubscribe", type, id);
120
120
  delete subscriptions[type][id];
121
121
 
122
122
  // Track unsubscription
@@ -152,7 +152,7 @@ const publish = (...args) => {
152
152
  const payload = args.pop();
153
153
  const type = args.join(".");
154
154
 
155
- console.log("node-event", "publish", type, payload);
155
+ logEvent("publish", type, payload);
156
156
  messages.set(type, payload);
157
157
 
158
158
  if (type === "__proto__" || type === "constructor" || type === "prototype") {
@@ -178,7 +178,6 @@ const publish = (...args) => {
178
178
  registry.callback(payload, registry);
179
179
  eventThroughput.labels(type).inc();
180
180
  } catch (err) {
181
- console.error("node-event", "error", type, err);
182
181
  const errorName = err instanceof Error ? err.name : "UnknownError";
183
182
  eventPublishErrors.labels(type, errorName).inc();
184
183
  } finally {
@@ -1,10 +1,11 @@
1
- import Joi from "joi";
1
+ import { AuthenticationError, AuthorizationError } from "../error";
2
2
  import express, { Request, Response } from "express";
3
- import jwt from "jsonwebtoken";
3
+
4
+ import Joi from "joi";
5
+ import Permission from "../models/Permission.model";
4
6
  import axios from "axios";
5
7
  import config from "../config";
6
- import { AuthenticationError, AuthorizationError } from "../error";
7
- import Permission from "../models/Permission.model";
8
+ import jwt from "jsonwebtoken";
8
9
 
9
10
  const router = express.Router();
10
11
 
@@ -17,33 +18,39 @@ if (!project) {
17
18
  router.post(
18
19
  "/",
19
20
  async (req: Request, res: Response): Promise<Response | void> => {
20
- const { appId, projectId, code, refreshToken, redirectUri, provider } =
21
- Joi.attempt(
22
- req.body,
23
- Joi.object({
24
- appId: Joi.string().required(),
25
- projectId: Joi.string().optional(),
26
- code: Joi.string().optional(),
27
- refreshToken: Joi.string().optional(),
28
- redirectUri: Joi.string().optional(),
29
- provider: Joi.string().required(),
30
- })
31
- .required()
32
- .options({ stripUnknown: true })
33
- ) as {
34
- appId: string;
35
- projectId?: string;
36
- code?: string;
37
- refreshToken?: string;
38
- redirectUri?: string;
39
- provider: string;
40
- };
21
+ const {
22
+ appId,
23
+ projectId,
24
+ code,
25
+ refreshToken,
26
+ redirectUri,
27
+ identityProvider,
28
+ } = Joi.attempt(
29
+ req.body,
30
+ Joi.object({
31
+ appId: Joi.string().required(),
32
+ projectId: Joi.string().optional(),
33
+ code: Joi.string().optional(),
34
+ refreshToken: Joi.string().optional(),
35
+ redirectUri: Joi.string().optional(),
36
+ identityProvider: Joi.string().required(),
37
+ })
38
+ .required()
39
+ .options({ stripUnknown: true })
40
+ ) as {
41
+ appId: string;
42
+ projectId?: string;
43
+ code?: string;
44
+ refreshToken?: string;
45
+ redirectUri?: string;
46
+ identityProvider: string;
47
+ };
41
48
 
42
49
  if (!code && !refreshToken) {
43
50
  return res.status(400).send("Missing OAuth Code and Refresh Token");
44
51
  }
45
52
 
46
- const providerConfig = project.oauth?.providers[provider] as {
53
+ const providerConfig = project.oauth?.providers[identityProvider] as {
47
54
  clientId: string;
48
55
  tokenUrl: string;
49
56
  userUrl: string;
@@ -68,7 +75,7 @@ router.post(
68
75
  params.append("client_id", providerConfig.clientId);
69
76
  params.append(
70
77
  "client_secret",
71
- process.env[`${provider.toUpperCase()}_CLIENT_SECRET`] as string
78
+ process.env[`${identityProvider.toUpperCase()}_CLIENT_SECRET`] as string
72
79
  );
73
80
  params.append("code", code);
74
81
  params.append("redirect_uri", redirectUri);
@@ -136,7 +143,7 @@ router.post(
136
143
  fallbackField: project.oauth?.jwt.identifier,
137
144
  });
138
145
  throw new Error(
139
- `Cannot find user identifier in ${provider} OAuth response`
146
+ `Cannot find user identifier in ${identityProvider} OAuth response`
140
147
  );
141
148
  }
142
149
 
@@ -153,7 +160,7 @@ router.post(
153
160
  sub: userId,
154
161
  iss: "nuc",
155
162
  aid: appId,
156
- provider: provider,
163
+ identityProvider: identityProvider,
157
164
  iat: Math.floor(Date.now() / 1000),
158
165
  },
159
166
  process.env.JWT_SECRET as string,
@@ -168,7 +175,7 @@ router.post(
168
175
  oid: permissions[0].organizationId,
169
176
  aid: appId,
170
177
  rls: permissions.map((permission) => permission.role),
171
- provider: provider,
178
+ identityProvider: identityProvider,
172
179
  iat: Math.floor(Date.now() / 1000),
173
180
  },
174
181
  process.env.JWT_SECRET as string,
@@ -181,7 +188,7 @@ router.post(
181
188
  sub: userId,
182
189
  iss: "nuc",
183
190
  aid: appId,
184
- provider: provider,
191
+ identityProvider: identityProvider,
185
192
  iat: Math.floor(Date.now() / 1000),
186
193
  },
187
194
  process.env.JWT_SECRET as string,
@@ -214,16 +221,16 @@ router.get("/user", async (req: Request, res: Response): Promise<Response> => {
214
221
 
215
222
  const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as {
216
223
  sub: string;
217
- provider: string;
224
+ identityProvider: string;
218
225
  };
219
226
  const userId = decoded.sub;
220
- const provider = decoded.provider;
227
+ const identityProvider = decoded.identityProvider;
221
228
 
222
- if (!userId || !provider) {
229
+ if (!userId || !identityProvider) {
223
230
  return res.status(401).end();
224
231
  }
225
232
 
226
- const providerConfig = project.oauth?.providers[provider] as {
233
+ const providerConfig = project.oauth?.providers[identityProvider] as {
227
234
  userUrl: string;
228
235
  userFields: {
229
236
  name: string;
@@ -250,7 +257,7 @@ router.get("/user", async (req: Request, res: Response): Promise<Response> => {
250
257
  const userFieldMapping = providerConfig.userFields;
251
258
  const userDetails = {
252
259
  id: userId,
253
- provider: provider,
260
+ identityProvider: identityProvider,
254
261
  name: (userResponse.data[userFieldMapping.name] as string) || null,
255
262
  displayName:
256
263
  (userResponse.data[userFieldMapping.displayName] as string) || null,