@canmingir/link-express 1.6.11 → 1.7.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.
Files changed (67) hide show
  1. package/.eslintrc.ts +29 -0
  2. package/bin/event-listener.ts +10 -0
  3. package/index.ts +2 -0
  4. package/package.json +66 -12
  5. package/{prepare.js → prepare.ts} +1 -1
  6. package/src/{authorization.js → authorization.ts} +31 -9
  7. package/src/config.ts +112 -0
  8. package/src/dynamodb.ts +24 -0
  9. package/src/{error.js → error.ts} +30 -11
  10. package/src/event/client/adapters/KafkaAdapter.ts +140 -0
  11. package/src/event/client/adapters/SocketAdapter.ts +59 -0
  12. package/src/event/client/adapters/TxEventQAdapter.ts +232 -0
  13. package/src/event/client/eventManager.ts +244 -0
  14. package/src/event/client/index.ts +50 -0
  15. package/src/event/client/metrics.ts +171 -0
  16. package/src/event/client/types/types.ts +43 -0
  17. package/src/event/index.ts +3 -0
  18. package/src/event/server/server.ts +55 -0
  19. package/src/event/src/Event.ts +201 -0
  20. package/src/express.ts +66 -0
  21. package/src/lib/{settings.js → settings.ts} +16 -4
  22. package/src/lib/test.ts +68 -0
  23. package/src/{logger.js → logger.ts} +11 -9
  24. package/src/metrics/{dbMetrics.js → dbMetrics.ts} +37 -14
  25. package/src/models/Organization.model.ts +27 -0
  26. package/src/models/Permission.model.ts +48 -0
  27. package/src/models/Project.model.ts +50 -0
  28. package/src/models/Settings.model.ts +31 -0
  29. package/src/models/{index.js → index.ts} +8 -8
  30. package/src/platform.ts +55 -0
  31. package/src/postgres.ts +309 -0
  32. package/src/routes/index.ts +8 -0
  33. package/src/routes/metrics.ts +13 -0
  34. package/src/routes/oauth.ts +267 -0
  35. package/src/routes/{organizations.js → organizations.ts} +10 -8
  36. package/src/routes/{permissions.js → permissions.ts} +8 -6
  37. package/src/routes/{projects.js → projects.ts} +22 -16
  38. package/src/routes/settings.ts +31 -0
  39. package/src/schemas/{Organization.js → Organization.ts} +2 -2
  40. package/src/schemas/{Permission.js → Permission.ts} +2 -2
  41. package/src/schemas/{Project.js → Project.ts} +2 -2
  42. package/src/schemas/index.ts +5 -0
  43. package/src/sequelize.ts +13 -0
  44. package/src/{test.js → test.ts} +11 -13
  45. package/src/types/Organization.ts +9 -0
  46. package/src/types/Permission.ts +13 -0
  47. package/src/types/Project.ts +14 -0
  48. package/src/types/index.ts +5 -0
  49. package/tsconfig.json +32 -0
  50. package/.eslintrc.js +0 -20
  51. package/index.js +0 -1
  52. package/src/config.js +0 -21
  53. package/src/dynamodb.js +0 -18
  54. package/src/express.js +0 -58
  55. package/src/lib/test.js +0 -69
  56. package/src/models/Organization.js +0 -17
  57. package/src/models/Permission.js +0 -33
  58. package/src/models/Project.js +0 -37
  59. package/src/models/Settings.js +0 -21
  60. package/src/openapi.js +0 -40
  61. package/src/platform.js +0 -56
  62. package/src/postgres.js +0 -308
  63. package/src/routes/index.js +0 -15
  64. package/src/routes/metrics.js +0 -12
  65. package/src/routes/oauth.js +0 -213
  66. package/src/routes/settings.js +0 -25
  67. package/src/schemas/index.js +0 -5
@@ -0,0 +1,43 @@
1
+ export type Callback<T = object> = (payload: T) => void;
2
+
3
+ export interface EventAdapter {
4
+ connect(): Promise<void>;
5
+ disconnect(): Promise<void>;
6
+ publish(type: string, payload: object): Promise<void>;
7
+ subscribe(type: string): Promise<void>;
8
+ unsubscribe(type: string): Promise<void>;
9
+ onMessage(handler: (type: string, payload: object) => void): void;
10
+ }
11
+
12
+ export interface BaseInitOptions {
13
+ type: "inMemory" | "kafka" | "txeventq";
14
+ }
15
+
16
+ export interface InMemoryOptions extends BaseInitOptions {
17
+ type: "inMemory";
18
+ host: string;
19
+ port?: number;
20
+ protocol: string;
21
+ }
22
+
23
+ export interface KafkaOptions extends BaseInitOptions {
24
+ type: "kafka";
25
+ clientId: string;
26
+ brokers: string[];
27
+ groupId: string;
28
+ }
29
+
30
+ export interface TxEventQOptions extends BaseInitOptions {
31
+ type: "txeventq";
32
+ connectString: string;
33
+ user: string;
34
+ password: string;
35
+ instantClientPath?: string;
36
+ walletPath?: string;
37
+ consumerName?: string;
38
+ batchSize?: number;
39
+ waitTime?: number;
40
+ topics?: string[];
41
+ }
42
+
43
+ export type InitOptions = InMemoryOptions | KafkaOptions | TxEventQOptions;
@@ -0,0 +1,3 @@
1
+ import { subscribe, publish } from "./src/Event";
2
+
3
+ export { subscribe, publish };
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from 'socket.io';
4
+ import { Socket } from 'socket.io';
5
+ import http from 'http';
6
+
7
+ const server = http.createServer();
8
+ const io = new Server(server, {
9
+ cors: { origin: '*' }
10
+ });
11
+
12
+ type Subscriptions = Record<string, Set<string>>;
13
+ const subscriptions: Subscriptions = {};
14
+
15
+ io.on('connection', (socket: Socket) => {
16
+ console.log('Client connected:', socket.id);
17
+
18
+ socket.on('subscribe', (type: string) => {
19
+ if (!subscriptions[type]) subscriptions[type] = new Set();
20
+ subscriptions[type].add(socket.id);
21
+ console.log(`Socket ${socket.id} subscribed to ${type}`);
22
+ });
23
+
24
+ socket.on('unsubscribe', (type: string) => {
25
+ if (subscriptions[type]) {
26
+ subscriptions[type].delete(socket.id);
27
+ if (subscriptions[type].size === 0) delete subscriptions[type];
28
+ console.log(`Socket ${socket.id} unsubscribed from ${type}`);
29
+ }
30
+ });
31
+
32
+ socket.on('publish', ({ type, payload }: { type: string; payload: object }) => {
33
+ console.log(`Publish: ${type}`, payload);
34
+ if (subscriptions[type]) {
35
+ subscriptions[type].forEach((sid) => {
36
+ if (sid !== socket.id) {
37
+ io.to(sid).emit('event', { type, payload });
38
+ }
39
+ });
40
+ }
41
+ });
42
+
43
+ socket.on('disconnect', () => {
44
+ Object.keys(subscriptions).forEach((type) => {
45
+ subscriptions[type].delete(socket.id);
46
+ if (subscriptions[type].size === 0) delete subscriptions[type];
47
+ });
48
+ console.log('Client disconnected:', socket.id);
49
+ });
50
+ });
51
+
52
+ const PORT = process.env.PORT || 8080;
53
+ server.listen(PORT, () => {
54
+ console.log(`Event server listening on port ${PORT}`);
55
+ });
@@ -0,0 +1,201 @@
1
+ import client from "prom-client";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ const subscriptions = {};
5
+ const messages = new Map();
6
+
7
+ const eventPublishCounter = new client.Counter({
8
+ name: "events_published_total",
9
+ help: "Total number of events published",
10
+ labelNames: ["event_type"],
11
+ });
12
+
13
+ const eventSubscriptionGauge = new client.Gauge({
14
+ name: "active_event_subscriptions",
15
+ help: "Number of active event subscriptions",
16
+ labelNames: ["event_type"],
17
+ });
18
+
19
+ const eventPublishDuration = new client.Histogram({
20
+ name: "event_publish_duration_seconds",
21
+ help: "Time taken to publish events",
22
+ labelNames: ["event_type"],
23
+ buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5],
24
+ });
25
+
26
+ // Track payload size for analysis
27
+ const eventPayloadSize = new client.Histogram({
28
+ name: "event_payload_size_bytes",
29
+ help: "Size of event payloads in bytes",
30
+ labelNames: ["event_type"],
31
+ buckets: [10, 100, 1000, 10000, 100000, 1000000],
32
+ });
33
+
34
+ // Track error rates
35
+ const eventPublishErrors = new client.Counter({
36
+ name: "event_publish_errors_total",
37
+ help: "Total number of event publish errors",
38
+ labelNames: ["event_type", "error_type"],
39
+ });
40
+
41
+ // Track callback processing duration
42
+ const callbackProcessingDuration = new client.Histogram({
43
+ name: "event_callback_duration_seconds",
44
+ help: "Time taken to process event callbacks",
45
+ labelNames: ["event_type"],
46
+ buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5],
47
+ });
48
+
49
+ // Track subscription rates
50
+ const subscriptionRate = new client.Counter({
51
+ name: "event_subscriptions_total",
52
+ help: "Total number of event subscriptions created",
53
+ labelNames: ["event_type"],
54
+ });
55
+
56
+ // Track unsubscription rates
57
+ const unsubscriptionRate = new client.Counter({
58
+ name: "event_unsubscriptions_total",
59
+ help: "Total number of event unsubscriptions",
60
+ labelNames: ["event_type"],
61
+ });
62
+
63
+ // Track throughput (events processed per second)
64
+ const eventThroughput = new client.Counter({
65
+ name: "event_callbacks_processed_total",
66
+ help: "Total number of event callbacks processed successfully",
67
+ labelNames: ["event_type"],
68
+ });
69
+
70
+ const colors = [
71
+ "red",
72
+ "green",
73
+ "yellow",
74
+ "blue",
75
+ "magenta",
76
+ "cyan",
77
+ "white",
78
+ "gray",
79
+ ];
80
+
81
+ function typeColor(type) {
82
+ const hash = (str) => {
83
+ let hash = 0;
84
+ for (let i = 0; i < str.length; i++) {
85
+ const char = str.charCodeAt(i);
86
+ hash = (hash << 5) - hash + char;
87
+ hash = hash & hash;
88
+ }
89
+ return Math.abs(hash);
90
+ };
91
+
92
+ const colorIndex = hash(type) % colors.length;
93
+ return colors[colorIndex];
94
+ }
95
+
96
+ const subscribe = (...args) => {
97
+ if (args.length < 2) {
98
+ throw new Error("subscribe requires at least 2 arguments");
99
+ }
100
+
101
+ const callback = args.pop();
102
+ const type = args.join(".");
103
+ const id = uuid();
104
+
105
+ console.debug("node-event", "subscribe", type, id);
106
+
107
+ if (type === "__proto__" || type === "constructor" || type === "prototype") {
108
+ throw new Error("Invalid subscription type");
109
+ }
110
+ if (!subscriptions[type]) {
111
+ subscriptions[type] = {};
112
+ }
113
+
114
+ const registry = {
115
+ id,
116
+ type,
117
+ callback,
118
+ unsubscribe: () => {
119
+ console.debug("node-event", "unsubscribe", type, id);
120
+ delete subscriptions[type][id];
121
+
122
+ // Track unsubscription
123
+ unsubscriptionRate.labels(type).inc();
124
+
125
+ if (Object.keys(subscriptions[type]).length === 0) {
126
+ delete subscriptions[type];
127
+ eventSubscriptionGauge.labels(type).set(0);
128
+ } else {
129
+ eventSubscriptionGauge
130
+ .labels(type)
131
+ .set(Object.keys(subscriptions[type]).length);
132
+ }
133
+ },
134
+ };
135
+
136
+ subscriptions[type][id] = registry;
137
+
138
+ // Update subscription metrics
139
+ subscriptionRate.labels(type).inc();
140
+ eventSubscriptionGauge
141
+ .labels(type)
142
+ .set(Object.keys(subscriptions[type]).length);
143
+
144
+ return registry;
145
+ };
146
+
147
+ const publish = (...args) => {
148
+ if (args.length < 2) {
149
+ throw new Error("publish requires at least 2 arguments");
150
+ }
151
+
152
+ const payload = args.pop();
153
+ const type = args.join(".");
154
+
155
+ console.log("node-event", "publish", type, payload);
156
+ messages.set(type, payload);
157
+
158
+ if (type === "__proto__" || type === "constructor" || type === "prototype") {
159
+ throw new Error("Invalid publish type");
160
+ }
161
+
162
+ // Track metrics for event publishing
163
+ const endTimer = eventPublishDuration.labels(type).startTimer();
164
+ eventPublishCounter.labels(type).inc();
165
+
166
+ // Track payload size
167
+ const payloadSize = JSON.stringify(payload).length;
168
+ eventPayloadSize.labels(type).observe(payloadSize);
169
+
170
+ Object.keys(subscriptions[type] || {}).forEach((key) => {
171
+ const registry = subscriptions[type][key];
172
+
173
+ setTimeout(() => {
174
+ const callbackTimer = callbackProcessingDuration
175
+ .labels(type)
176
+ .startTimer();
177
+ try {
178
+ registry.callback(payload, registry);
179
+ eventThroughput.labels(type).inc();
180
+ } catch (err) {
181
+ console.error("node-event", "error", type, err);
182
+ const errorName = err instanceof Error ? err.name : "UnknownError";
183
+ eventPublishErrors.labels(type, errorName).inc();
184
+ } finally {
185
+ callbackTimer();
186
+ }
187
+ }, 0);
188
+ });
189
+
190
+ endTimer();
191
+ };
192
+
193
+ function last(type, init) {
194
+ if (messages.has(type)) {
195
+ return messages.get(type);
196
+ } else {
197
+ return init;
198
+ }
199
+ }
200
+
201
+ export { subscribe, publish, messages, last, client };
package/src/express.ts ADDED
@@ -0,0 +1,66 @@
1
+ import express, { Request, Response, NextFunction } from "express";
2
+ import "express-async-errors";
3
+ import cors from "cors";
4
+ import morgan from "morgan";
5
+ import helmet from "helmet";
6
+ import * as error from "./error";
7
+ import * as authorization from "./authorization";
8
+ import settings from "./routes/settings";
9
+ import metrics from "./routes/metrics";
10
+ import { getConfig } from "./config";
11
+
12
+ const app = express();
13
+
14
+ const appConfig = getConfig();
15
+
16
+ app.use(helmet());
17
+ app.use(cors());
18
+ app.use(morgan("tiny"));
19
+
20
+ app.use(
21
+ express.json(),
22
+ (err: Error, _req: Request, res: Response, next: NextFunction) =>
23
+ err ? res.status(422).end() : next()
24
+ );
25
+
26
+ if (appConfig.project) {
27
+ import("./routes/oauth.ts").then((oauthModule) => {
28
+ const oauth = oauthModule.default || oauthModule;
29
+ app.use(
30
+ "/oauth",
31
+ express.urlencoded(),
32
+ (err: Error, _req: Request, res: Response, next: NextFunction) =>
33
+ err ? res.status(422).end() : next(),
34
+ oauth
35
+ );
36
+ });
37
+ }
38
+
39
+ app.use("/metrics", metrics);
40
+
41
+ setImmediate(async () => {
42
+ process.env.PROFILE === "TEST" && app.use(authorization.verify);
43
+
44
+ if (appConfig.project) {
45
+ const [permissionsModule, organizationsModule, projectsModule] =
46
+ await Promise.all([
47
+ import("./routes/permissions.ts"),
48
+ import("./routes/organizations.ts"),
49
+ import("./routes/projects.ts"),
50
+ ]);
51
+
52
+ const permissions = permissionsModule.default || permissionsModule;
53
+ const organizations = organizationsModule.default || organizationsModule;
54
+ const projects = projectsModule.default || projectsModule;
55
+
56
+ app.use("/projects", projects);
57
+ app.use("/organizations", organizations);
58
+ app.use("/permissions", permissions);
59
+ app.use("/projects/:projectId/settings", settings);
60
+ }
61
+
62
+ app.use((_req: Request, res: Response) => res.status(404).end());
63
+ app.use(error.handle);
64
+ });
65
+
66
+ export default app;
@@ -1,6 +1,10 @@
1
- const Settings = require("../models/Settings");
1
+ import Settings from "../models/Settings.model";
2
2
 
3
- async function get({ projectId }) {
3
+ async function get({
4
+ projectId,
5
+ }: {
6
+ projectId: string;
7
+ }): Promise<Record<string, unknown>> {
4
8
  const settingsInstance = await Settings.findOne({
5
9
  where: { projectId },
6
10
  });
@@ -12,7 +16,15 @@ async function get({ projectId }) {
12
16
  }
13
17
  }
14
18
 
15
- async function upsert({ projectId }, settings) {
19
+ async function upsert(
20
+ {
21
+ projectId,
22
+ }: {
23
+ projectId: string;
24
+ },
25
+ settings: Record<string, unknown>,
26
+ _params?: Record<string, unknown>
27
+ ): Promise<void> {
16
28
  const settingsInstance = await Settings.findOne({
17
29
  where: { projectId },
18
30
  });
@@ -33,4 +45,4 @@ async function upsert({ projectId }, settings) {
33
45
  }
34
46
  }
35
47
 
36
- module.exports = { get, upsert };
48
+ export { get, upsert };
@@ -0,0 +1,68 @@
1
+ import dotenv from "dotenv";
2
+ import platform from "../platform";
3
+ import { Sequelize } from "sequelize";
4
+
5
+ dotenv.config({ path: ".env.test" });
6
+
7
+ platform.init({
8
+ project: {
9
+ name: "test",
10
+ version: "1.0.0",
11
+ oauth: {
12
+ jwt: {
13
+ identifier: "email",
14
+ },
15
+ providers: {
16
+ github: {
17
+ tokenUrl: "https://github.com/login/oauth/access_token",
18
+ userUrl: "https://api.github.com/user",
19
+ clientId: "0c2844d3d19dc9293fc5",
20
+ redirectUri: "http://localhost:5173/callback",
21
+ userIdentifier: "id",
22
+ userFields: {
23
+ name: "name",
24
+ displayName: "login",
25
+ avatarUrl: "avatar_url",
26
+ email: "email",
27
+ },
28
+ },
29
+ },
30
+ },
31
+ },
32
+ postgres: {
33
+ uri: "sqlite::memory:",
34
+ debug: true,
35
+ sync: false,
36
+ },
37
+ });
38
+
39
+ import { init } from "../models";
40
+
41
+ async function reset(): Promise<void> {
42
+ const { sequelize }: { sequelize: Sequelize } = await import("../postgres");
43
+
44
+ if (await init()) {
45
+ await sequelize.sync({ force: true });
46
+ }
47
+
48
+ await sequelize.models.Organization.destroy({ truncate: true });
49
+ await sequelize.models.Project.destroy({ truncate: true });
50
+ await sequelize.models.Permission.destroy({ truncate: true });
51
+ await sequelize.models.Settings.destroy({ truncate: true });
52
+
53
+ async function seed(): Promise<void> {
54
+ const { seed: organizations } = await import("../seeds/Organization.json");
55
+ const { seed: permissions } = await import("../seeds/Permission.json");
56
+ const { seed: projects } = await import("../seeds/Project.json");
57
+ const { seed: settings } = await import("../seeds/Settings.json");
58
+
59
+ await sequelize.models.Organization.bulkCreate(organizations);
60
+ await sequelize.models.Project.bulkCreate(projects);
61
+ await sequelize.models.Permission.bulkCreate(permissions);
62
+ await sequelize.models.Settings.bulkCreate(settings);
63
+ }
64
+
65
+ await seed();
66
+ }
67
+
68
+ export { reset };
@@ -1,20 +1,22 @@
1
- const { ecsFormat } = require("@elastic/ecs-pino-format");
2
- const pino = require("pino");
3
- const pinoElastic = require("pino-elasticsearch");
1
+ import { ecsFormat } from "@elastic/ecs-pino-format";
2
+ import pino from "pino";
3
+ import pinoElastic from "pino-elasticsearch";
4
+ import { getConfig } from "./config";
4
5
 
5
- const config = require("./config");
6
+ const { logger: loggerConfig, project } = getConfig();
6
7
 
7
- const { logger: loggerConfig, project } = config();
8
+ if (!loggerConfig || !project) {
9
+ throw new Error("Logger and project configuration are required");
10
+ }
8
11
 
9
- const streams = [{ stream: process.stdout }];
12
+ const streams: pino.StreamEntry[] = [{ stream: process.stdout }];
10
13
 
11
14
  const streamToElastic = pinoElastic({
12
15
  index: loggerConfig.elasticsearch.index,
13
- consistency: loggerConfig.elasticsearch.consistency || "one",
14
16
  node: loggerConfig.elasticsearch.node,
15
17
  esVersion: loggerConfig.elasticsearch.esVersion || 8,
16
18
  flushBytes: loggerConfig.elasticsearch.flushBytes || 1000,
17
- });
19
+ } as Parameters<typeof pinoElastic>[0]);
18
20
 
19
21
  streams.push({ stream: streamToElastic });
20
22
 
@@ -31,4 +33,4 @@ const logger = pino(
31
33
  pino.multistream(streams)
32
34
  );
33
35
 
34
- module.exports = logger;
36
+ export default logger;
@@ -1,6 +1,30 @@
1
- const promClient = require("prom-client");
1
+ import promClient from "prom-client";
2
+
3
+ interface PushgatewayConfig {
4
+ url: string;
5
+ jobName: string;
6
+ instance?: string;
7
+ interval: number;
8
+ }
9
+
10
+ interface MetricsConfig {
11
+ url?: string;
12
+ pushGateway: {
13
+ jobName?: string;
14
+ instance?: string;
15
+ };
16
+ interval?: number;
17
+ }
2
18
 
3
19
  class DBMetrics {
20
+ registry: promClient.Registry;
21
+ pushgatewayInterval: NodeJS.Timeout | null;
22
+ pushgatewayConfig: PushgatewayConfig | null;
23
+ dbReadOps: promClient.Counter<string>;
24
+ dbWriteOps: promClient.Counter<string>;
25
+ dbReadLatency: promClient.Histogram<string>;
26
+ dbWriteLatency: promClient.Histogram<string>;
27
+
4
28
  constructor() {
5
29
  this.registry = new promClient.Registry();
6
30
  this.pushgatewayInterval = null;
@@ -33,17 +57,17 @@ class DBMetrics {
33
57
  });
34
58
  }
35
59
 
36
- recordDbRead() {
60
+ recordDbRead(): () => number {
37
61
  this.dbReadOps.inc();
38
62
  return this.dbReadLatency.startTimer();
39
63
  }
40
64
 
41
- recordDbWrite() {
65
+ recordDbWrite(): () => number {
42
66
  this.dbWriteOps.inc();
43
67
  return this.dbWriteLatency.startTimer();
44
68
  }
45
69
 
46
- startPushgateway(metrics = {}) {
70
+ startPushgateway(metrics: MetricsConfig): void {
47
71
  this.pushgatewayConfig = {
48
72
  url: metrics.url || "http://localhost:9091",
49
73
  jobName: metrics.pushGateway.jobName || "api",
@@ -62,19 +86,18 @@ class DBMetrics {
62
86
  );
63
87
  }
64
88
 
65
- stopPushgateway() {
89
+ stopPushgateway(): void {
66
90
  if (this.pushgatewayInterval) {
67
91
  clearInterval(this.pushgatewayInterval);
68
- this.pushgatewayInterval = undefined;
92
+ this.pushgatewayInterval = null;
69
93
  console.log("Stopped pushing metrics to Pushgateway");
70
94
  }
71
95
  }
72
96
 
73
- async pushMetricsToGateway() {
97
+ async pushMetricsToGateway(): Promise<void> {
74
98
  if (!this.pushgatewayConfig) {
75
- throw new Error(
76
- "Pushgateway not configured. Call startPushgateway() first."
77
- );
99
+ console.error("[DBMetrics] Pushgateway not configured");
100
+ return;
78
101
  }
79
102
 
80
103
  try {
@@ -95,15 +118,15 @@ class DBMetrics {
95
118
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
96
119
  }
97
120
 
98
- console.log("Metrics pushed to Pushgateway successfully");
121
+ console.log("[DBMetrics] Metrics pushed to Pushgateway successfully");
99
122
  } catch (err) {
100
- console.error("Failed to push metrics to Pushgateway:", err);
123
+ console.error("[DBMetrics] Failed to push metrics to Pushgateway:", err);
101
124
  }
102
125
  }
103
126
 
104
- getPushgatewayConfig() {
127
+ getPushgatewayConfig(): PushgatewayConfig | null {
105
128
  return this.pushgatewayConfig;
106
129
  }
107
130
  }
108
131
 
109
- module.exports = { DBMetrics };
132
+ export { DBMetrics };
@@ -0,0 +1,27 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ DataType,
6
+ Default,
7
+ PrimaryKey,
8
+ AllowNull,
9
+ } from "sequelize-typescript";
10
+
11
+ @Table({
12
+ tableName: "Organization",
13
+ timestamps: false,
14
+ underscored: true,
15
+ })
16
+ class Organization extends Model {
17
+ @PrimaryKey
18
+ @Default(DataType.UUIDV4)
19
+ @Column(DataType.UUID)
20
+ declare id: string;
21
+
22
+ @AllowNull(false)
23
+ @Column(DataType.STRING)
24
+ declare name: string;
25
+ }
26
+
27
+ export default Organization;
@@ -0,0 +1,48 @@
1
+ import {
2
+ Table,
3
+ Column,
4
+ Model,
5
+ DataType,
6
+ Default,
7
+ PrimaryKey,
8
+ AllowNull,
9
+ ForeignKey,
10
+ } from "sequelize-typescript";
11
+ import Organization from "./Organization.model";
12
+ import Project from "./Project.model";
13
+
14
+ @Table({
15
+ tableName: "Permission",
16
+ timestamps: false,
17
+ underscored: true,
18
+ })
19
+ class Permission extends Model {
20
+ @PrimaryKey
21
+ @Default(DataType.UUIDV4)
22
+ @Column(DataType.UUID)
23
+ declare id: string;
24
+
25
+ @AllowNull(false)
26
+ @Column(DataType.UUID)
27
+ declare appId: string;
28
+
29
+ @AllowNull(false)
30
+ @ForeignKey(() => Organization)
31
+ @Column(DataType.UUID)
32
+ declare organizationId: string;
33
+
34
+ @AllowNull(false)
35
+ @ForeignKey(() => Project)
36
+ @Column(DataType.UUID)
37
+ declare projectId: string;
38
+
39
+ @AllowNull(false)
40
+ @Column(DataType.STRING)
41
+ declare userId: string;
42
+
43
+ @AllowNull(false)
44
+ @Column(DataType.STRING)
45
+ declare role: string;
46
+ }
47
+
48
+ export default Permission;