@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 +0 -0
- package/package.json +1 -1
- package/src/event/client/eventManager.ts +6 -0
- package/src/event/eventLogger.ts +74 -0
- package/src/event/src/Event.ts +3 -4
- package/src/routes/oauth.ts +43 -36
package/bin/cli.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|
package/src/event/src/Event.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {
|
package/src/routes/oauth.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AuthenticationError, AuthorizationError } from "../error";
|
|
2
2
|
import express, { Request, Response } from "express";
|
|
3
|
-
|
|
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
|
|
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 {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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[
|
|
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[`${
|
|
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 ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
224
|
+
identityProvider: string;
|
|
218
225
|
};
|
|
219
226
|
const userId = decoded.sub;
|
|
220
|
-
const
|
|
227
|
+
const identityProvider = decoded.identityProvider;
|
|
221
228
|
|
|
222
|
-
if (!userId || !
|
|
229
|
+
if (!userId || !identityProvider) {
|
|
223
230
|
return res.status(401).end();
|
|
224
231
|
}
|
|
225
232
|
|
|
226
|
-
const providerConfig = project.oauth?.providers[
|
|
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
|
-
|
|
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,
|