pulsewatch-server 1.0.61 → 1.0.62

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.
@@ -1 +1 @@
1
- {"version":3,"file":"pulse-config.d.ts","sourceRoot":"","sources":["../../src/routes/pulse-config.ts"],"names":[],"mappings":"AAGA,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAAE,EAAE,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,8CAiNrE"}
1
+ {"version":3,"file":"pulse-config.d.ts","sourceRoot":"","sources":["../../src/routes/pulse-config.ts"],"names":[],"mappings":"AAGA,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAAE,EAAE,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,8CAsQrE"}
@@ -1,14 +1,16 @@
1
- import { Router } from 'express';
2
- import { eq } from 'drizzle-orm';
1
+ import { Router } from "express";
2
+ import { eq } from "drizzle-orm";
3
3
  export function createPulseConfigRoutes(deps) {
4
4
  const router = Router();
5
5
  const { db, schema } = deps;
6
- // Get Pulse notification configuration
7
- router.get('/config', async (req, res) => {
6
+ const table = schema.pulseNotificationConfig;
7
+ // --------------------------------------------------
8
+ // GET CONFIG
9
+ // --------------------------------------------------
10
+ router.get("/config", async (_req, res) => {
8
11
  try {
9
- const configs = await db.select().from(schema.pulseNotificationConfig).limit(1);
12
+ const configs = await db.select().from(table).limit(1);
10
13
  if (configs.length === 0) {
11
- // Return default config if none exists
12
14
  return res.json({
13
15
  id: null,
14
16
  is_enabled: true,
@@ -19,165 +21,202 @@ export function createPulseConfigRoutes(deps) {
19
21
  updated_at: null,
20
22
  });
21
23
  }
22
- res.json(configs[0]);
24
+ const c = configs[0];
25
+ // Convert to API snake_case
26
+ return res.json({
27
+ id: c.id,
28
+ is_enabled: c.isEnabled,
29
+ consecutive_down_threshold: c.consecutiveDownThreshold,
30
+ consecutive_up_threshold: c.consecutiveUpThreshold,
31
+ to_emails: c.toEmails,
32
+ created_at: c.createdAt,
33
+ updated_at: c.updatedAt,
34
+ });
23
35
  }
24
36
  catch (error) {
25
- console.error('Error fetching Pulse config:', error);
26
- res.status(500).json({ error: 'Failed to fetch configuration' });
37
+ console.error("Error fetching Pulse config:", error);
38
+ res.status(500).json({ error: "Failed to fetch configuration" });
27
39
  }
28
40
  });
29
- // Update Pulse notification configuration
30
- router.put('/config', async (req, res) => {
41
+ // --------------------------------------------------
42
+ // UPSERT CONFIG
43
+ // --------------------------------------------------
44
+ router.put("/config", async (req, res) => {
31
45
  try {
32
- const { is_enabled, consecutive_down_threshold, consecutive_up_threshold } = req.body;
33
- // Validation
34
- if (consecutive_down_threshold !== undefined) {
35
- if (typeof consecutive_down_threshold !== 'number' || consecutive_down_threshold < 1 || consecutive_down_threshold > 100) {
36
- return res.status(400).json({ error: 'consecutive_down_threshold must be between 1 and 100' });
37
- }
46
+ const { is_enabled, consecutive_down_threshold, consecutive_up_threshold, to_emails, } = req.body;
47
+ // ---------- Validation ----------
48
+ if (consecutive_down_threshold !== undefined &&
49
+ (typeof consecutive_down_threshold !== "number" ||
50
+ consecutive_down_threshold < 1 ||
51
+ consecutive_down_threshold > 100)) {
52
+ return res.status(400).json({
53
+ error: "consecutive_down_threshold must be between 1 and 100",
54
+ });
38
55
  }
39
- if (consecutive_up_threshold !== undefined) {
40
- if (typeof consecutive_up_threshold !== 'number' || consecutive_up_threshold < 1 || consecutive_up_threshold > 100) {
41
- return res.status(400).json({ error: 'consecutive_up_threshold must be between 1 and 100' });
42
- }
56
+ if (consecutive_up_threshold !== undefined &&
57
+ (typeof consecutive_up_threshold !== "number" ||
58
+ consecutive_up_threshold < 1 ||
59
+ consecutive_up_threshold > 100)) {
60
+ return res.status(400).json({
61
+ error: "consecutive_up_threshold must be between 1 and 100",
62
+ });
63
+ }
64
+ if (to_emails !== undefined && !Array.isArray(to_emails)) {
65
+ return res.status(400).json({
66
+ error: "to_emails must be an array",
67
+ });
43
68
  }
44
- // Check if config exists
45
- const existingConfigs = await db.select().from(schema.pulseNotificationConfig).limit(1);
69
+ // ---------- Build update (camelCase for Drizzle) ----------
70
+ const updateData = {
71
+ ...(is_enabled !== undefined && { isEnabled: is_enabled }),
72
+ ...(consecutive_down_threshold !== undefined && {
73
+ consecutiveDownThreshold: consecutive_down_threshold,
74
+ }),
75
+ ...(consecutive_up_threshold !== undefined && {
76
+ consecutiveUpThreshold: consecutive_up_threshold,
77
+ }),
78
+ ...(to_emails !== undefined && { toEmails: to_emails }),
79
+ };
80
+ // Always update timestamp if something changes
81
+ if (Object.keys(updateData).length > 0) {
82
+ updateData.updatedAt = new Date();
83
+ }
84
+ // ---------- Get existing ----------
85
+ const existing = await db.select().from(table).limit(1);
46
86
  let result;
47
- if (existingConfigs.length === 0) {
48
- // Create new config with defaults
87
+ // ---------- INSERT if not exists ----------
88
+ if (existing.length === 0) {
49
89
  const insertData = {
50
- is_enabled: is_enabled !== undefined ? is_enabled : true,
51
- consecutive_down_threshold: consecutive_down_threshold !== undefined ? consecutive_down_threshold : 3,
52
- consecutive_up_threshold: consecutive_up_threshold !== undefined ? consecutive_up_threshold : 3,
53
- created_at: new Date(),
54
- updated_at: new Date(),
90
+ isEnabled: is_enabled ?? true,
91
+ consecutiveDownThreshold: consecutive_down_threshold ?? 3,
92
+ consecutiveUpThreshold: consecutive_up_threshold ?? 3,
93
+ toEmails: to_emails ?? [],
94
+ createdAt: new Date(),
95
+ updatedAt: new Date(),
55
96
  };
56
- result = await db.insert(schema.pulseNotificationConfig).values(insertData).returning();
97
+ result = await db.insert(table).values(insertData).returning();
57
98
  }
58
99
  else {
59
- // Update existing config - only include fields that are being updated
60
- const updateData = {};
61
- if (is_enabled !== undefined)
62
- updateData.is_enabled = is_enabled;
63
- if (consecutive_down_threshold !== undefined)
64
- updateData.consecutive_down_threshold = consecutive_down_threshold;
65
- if (consecutive_up_threshold !== undefined)
66
- updateData.consecutive_up_threshold = consecutive_up_threshold;
67
- // Check if there are any actual changes besides updated_at
68
100
  if (Object.keys(updateData).length === 0) {
69
- // No changes, return existing config
70
- return res.json({
71
- message: 'No changes to update',
72
- data: existingConfigs[0],
101
+ return res.status(400).json({
102
+ message: "No fields to update",
73
103
  });
74
104
  }
75
- // Add updated_at timestamp
76
- updateData.updated_at = new Date();
77
- console.log(updateData, "updateData");
78
105
  result = await db
79
- .update(schema.pulseNotificationConfig)
106
+ .update(table)
80
107
  .set(updateData)
81
- .where(eq(schema.pulseNotificationConfig.id, existingConfigs[0].id))
108
+ .where(eq(table.id, existing[0].id))
82
109
  .returning();
83
110
  }
84
- res.json({
85
- message: 'Configuration updated successfully',
86
- data: result[0],
111
+ const c = result[0];
112
+ return res.json({
113
+ message: "Configuration updated successfully",
114
+ data: {
115
+ id: c.id,
116
+ is_enabled: c.isEnabled,
117
+ consecutive_down_threshold: c.consecutiveDownThreshold,
118
+ consecutive_up_threshold: c.consecutiveUpThreshold,
119
+ to_emails: c.toEmails,
120
+ created_at: c.createdAt,
121
+ updated_at: c.updatedAt,
122
+ },
87
123
  });
88
124
  }
89
125
  catch (error) {
90
- console.error('Error updating Pulse config:', error);
91
- res.status(500).json({ error: 'Failed to update configuration' });
126
+ console.error("Error updating Pulse config:", error);
127
+ res.status(500).json({ error: "Failed to update configuration" });
92
128
  }
93
129
  });
94
- // Add email to Pulse notification configuration
95
- router.post('/config/emails', async (req, res) => {
130
+ // --------------------------------------------------
131
+ // ADD EMAIL
132
+ // --------------------------------------------------
133
+ router.post("/config/emails", async (req, res) => {
96
134
  try {
97
135
  const { email } = req.body;
98
136
  if (!email) {
99
- return res.status(400).json({ error: 'Email is required' });
137
+ return res.status(400).json({ error: "Email is required" });
100
138
  }
101
- // Validate email format
102
139
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
103
140
  if (!emailRegex.test(email)) {
104
- return res.status(400).json({ error: 'Invalid email format' });
141
+ return res.status(400).json({ error: "Invalid email format" });
105
142
  }
106
- // Get current config
107
- const configs = await db.select().from(schema.pulseNotificationConfig).limit(1);
108
- if (configs.length === 0) {
109
- // Create new config with this email
110
- const result = await db.insert(schema.pulseNotificationConfig).values({
111
- is_enabled: true,
112
- consecutive_down_threshold: 3,
113
- consecutive_up_threshold: 3,
114
- to_emails: [email],
115
- created_at: new Date(),
116
- updated_at: new Date(),
117
- }).returning();
118
- return res.json({
119
- message: 'Email added successfully',
120
- data: result[0],
121
- });
143
+ const existing = await db.select().from(table).limit(1);
144
+ let config;
145
+ if (existing.length === 0) {
146
+ const inserted = await db
147
+ .insert(table)
148
+ .values({
149
+ isEnabled: true,
150
+ consecutiveDownThreshold: 3,
151
+ consecutiveUpThreshold: 3,
152
+ toEmails: [email],
153
+ createdAt: new Date(),
154
+ updatedAt: new Date(),
155
+ })
156
+ .returning();
157
+ config = inserted[0];
122
158
  }
123
- const currentConfig = configs[0];
124
- const currentEmails = currentConfig.to_emails || [];
125
- if (currentEmails.includes(email)) {
126
- return res.status(400).json({ error: 'Email already exists' });
159
+ else {
160
+ const current = existing[0];
161
+ const emails = current.toEmails || [];
162
+ if (emails.includes(email)) {
163
+ return res.status(400).json({ error: "Email already exists" });
164
+ }
165
+ const updated = await db
166
+ .update(table)
167
+ .set({
168
+ toEmails: [...emails, email],
169
+ updatedAt: new Date(),
170
+ })
171
+ .where(eq(table.id, current.id))
172
+ .returning();
173
+ config = updated[0];
127
174
  }
128
- const updatedEmails = [...currentEmails, email];
129
- const result = await db
130
- .update(schema.pulseNotificationConfig)
131
- .set({
132
- to_emails: updatedEmails,
133
- updated_at: new Date(),
134
- })
135
- .where(eq(schema.pulseNotificationConfig.id, currentConfig.id))
136
- .returning();
137
175
  res.json({
138
- message: 'Email added successfully',
139
- data: result[0],
176
+ message: "Email added successfully",
177
+ data: config,
140
178
  });
141
179
  }
142
180
  catch (error) {
143
- console.error('Error adding email:', error);
144
- res.status(500).json({ error: 'Failed to add email' });
181
+ console.error("Error adding email:", error);
182
+ res.status(500).json({ error: "Failed to add email" });
145
183
  }
146
184
  });
147
- // Remove email from Pulse notification configuration
148
- router.delete('/config/emails/:email', async (req, res) => {
185
+ // --------------------------------------------------
186
+ // REMOVE EMAIL
187
+ // --------------------------------------------------
188
+ router.delete("/config/emails/:email", async (req, res) => {
149
189
  try {
150
190
  const { email } = req.params;
151
191
  if (!email) {
152
- return res.status(400).json({ error: 'Email is required' });
192
+ return res.status(400).json({ error: "Email is required" });
153
193
  }
154
- // Get current config
155
- const configs = await db.select().from(schema.pulseNotificationConfig).limit(1);
156
- if (configs.length === 0) {
157
- return res.status(404).json({ error: 'Configuration not found' });
194
+ const existing = await db.select().from(table).limit(1);
195
+ if (existing.length === 0) {
196
+ return res.status(404).json({ error: "Configuration not found" });
158
197
  }
159
- const currentConfig = configs[0];
160
- const currentEmails = currentConfig.to_emails || [];
161
- if (!currentEmails.includes(email)) {
162
- return res.status(404).json({ error: 'Email not found in configuration' });
198
+ const current = existing[0];
199
+ const emails = current.toEmails || [];
200
+ if (!emails.includes(email)) {
201
+ return res.status(404).json({ error: "Email not found in configuration" });
163
202
  }
164
- const updatedEmails = currentEmails.filter((e) => e !== email);
203
+ const updatedEmails = emails.filter((e) => e !== email);
165
204
  const result = await db
166
- .update(schema.pulseNotificationConfig)
205
+ .update(table)
167
206
  .set({
168
- to_emails: updatedEmails,
169
- updated_at: new Date(),
207
+ toEmails: updatedEmails,
208
+ updatedAt: new Date(),
170
209
  })
171
- .where(eq(schema.pulseNotificationConfig.id, currentConfig.id))
210
+ .where(eq(table.id, current.id))
172
211
  .returning();
173
212
  res.json({
174
- message: 'Email removed successfully',
213
+ message: "Email removed successfully",
175
214
  data: result[0],
176
215
  });
177
216
  }
178
217
  catch (error) {
179
- console.error('Error removing email:', error);
180
- res.status(500).json({ error: 'Failed to remove email' });
218
+ console.error("Error removing email:", error);
219
+ res.status(500).json({ error: "Failed to remove email" });
181
220
  }
182
221
  });
183
222
  return router;
@@ -1 +1 @@
1
- {"version":3,"file":"pulse-config.js","sourceRoot":"","sources":["../../src/routes/pulse-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,MAAM,UAAU,uBAAuB,CAAC,IAA8B;IAClE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAE5B,uCAAuC;IACvC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACxD,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEhF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,uCAAuC;gBACvC,OAAO,GAAG,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,IAAI;oBACR,UAAU,EAAE,IAAI;oBAChB,0BAA0B,EAAE,CAAC;oBAC7B,wBAAwB,EAAE,CAAC;oBAC3B,SAAS,EAAE,EAAE;oBACb,UAAU,EAAE,IAAI;oBAChB,UAAU,EAAE,IAAI;iBACnB,CAAC,CAAC;YACP,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;QACrE,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0CAA0C;IAC1C,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACxD,IAAI,CAAC;YACD,MAAM,EAAE,UAAU,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAEtF,aAAa;YACb,IAAI,0BAA0B,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,OAAO,0BAA0B,KAAK,QAAQ,IAAI,0BAA0B,GAAG,CAAC,IAAI,0BAA0B,GAAG,GAAG,EAAE,CAAC;oBACvH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sDAAsD,EAAE,CAAC,CAAC;gBACnG,CAAC;YACL,CAAC;YAED,IAAI,wBAAwB,KAAK,SAAS,EAAE,CAAC;gBACzC,IAAI,OAAO,wBAAwB,KAAK,QAAQ,IAAI,wBAAwB,GAAG,CAAC,IAAI,wBAAwB,GAAG,GAAG,EAAE,CAAC;oBACjH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oDAAoD,EAAE,CAAC,CAAC;gBACjG,CAAC;YACL,CAAC;YAED,yBAAyB;YACzB,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAExF,IAAI,MAAM,CAAC;YACX,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,kCAAkC;gBAClC,MAAM,UAAU,GAAG;oBACf,UAAU,EAAE,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;oBACxD,0BAA0B,EAAE,0BAA0B,KAAK,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC;oBACrG,wBAAwB,EAAE,wBAAwB,KAAK,SAAS,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;oBAE/F,UAAU,EAAE,IAAI,IAAI,EAAE;oBACtB,UAAU,EAAE,IAAI,IAAI,EAAE;iBACzB,CAAC;gBAEF,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,EAAE,CAAC;YAC5F,CAAC;iBAAM,CAAC;gBACJ,sEAAsE;gBACtE,MAAM,UAAU,GAAQ,EAAE,CAAC;gBAE3B,IAAI,UAAU,KAAK,SAAS;oBAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC;gBACjE,IAAI,0BAA0B,KAAK,SAAS;oBAAE,UAAU,CAAC,0BAA0B,GAAG,0BAA0B,CAAC;gBACjH,IAAI,wBAAwB,KAAK,SAAS;oBAAE,UAAU,CAAC,wBAAwB,GAAG,wBAAwB,CAAC;gBAE3G,2DAA2D;gBAC3D,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvC,qCAAqC;oBACrC,OAAO,GAAG,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,sBAAsB;wBAC/B,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;qBAC3B,CAAC,CAAC;gBACP,CAAC;gBAED,2BAA2B;gBAC3B,UAAU,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAC,YAAY,CAAC,CAAC;gBAErC,MAAM,GAAG,MAAM,EAAE;qBACZ,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC;qBACtC,GAAG,CAAC,UAAU,CAAC;qBACf,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;qBACnE,SAAS,EAAE,CAAC;YACrB,CAAC;YAED,GAAG,CAAC,IAAI,CAAC;gBACL,OAAO,EAAE,oCAAoC;gBAC7C,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;aAClB,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YACrD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;QACtE,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAChE,IAAI,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;YAE3B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YAChE,CAAC;YAED,wBAAwB;YACxB,MAAM,UAAU,GAAG,4BAA4B,CAAC;YAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,qBAAqB;YACrB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEhF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,oCAAoC;gBACpC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,MAAM,CAAC;oBAClE,UAAU,EAAE,IAAI;oBAChB,0BAA0B,EAAE,CAAC;oBAC7B,wBAAwB,EAAE,CAAC;oBAC3B,SAAS,EAAE,CAAC,KAAK,CAAC;oBAClB,UAAU,EAAE,IAAI,IAAI,EAAE;oBACtB,UAAU,EAAE,IAAI,IAAI,EAAE;iBACzB,CAAC,CAAC,SAAS,EAAE,CAAC;gBAEf,OAAO,GAAG,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,0BAA0B;oBACnC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;iBAClB,CAAC,CAAC;YACP,CAAC;YAED,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC;YAEpD,IAAI,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,MAAM,aAAa,GAAG,CAAC,GAAG,aAAa,EAAE,KAAK,CAAC,CAAC;YAEhD,MAAM,MAAM,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC;iBACtC,GAAG,CAAC;gBACD,SAAS,EAAE,aAAa;gBACxB,UAAU,EAAE,IAAI,IAAI,EAAE;aACzB,CAAC;iBACD,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;iBAC9D,SAAS,EAAE,CAAC;YAEjB,GAAG,CAAC,IAAI,CAAC;gBACL,OAAO,EAAE,0BAA0B;gBACnC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;aAClB,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,qDAAqD;IACrD,MAAM,CAAC,MAAM,CAAC,uBAAuB,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACzE,IAAI,CAAC;YACD,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAE7B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;YAChE,CAAC;YAED,qBAAqB;YACrB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAEhF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC;YAEpD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAC;YAC/E,CAAC;YAED,MAAM,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;YAEvE,MAAM,MAAM,GAAG,MAAM,EAAE;iBAClB,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC;iBACtC,GAAG,CAAC;gBACD,SAAS,EAAE,aAAa;gBACxB,UAAU,EAAE,IAAI,IAAI,EAAE;aACzB,CAAC;iBACD,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;iBAC9D,SAAS,EAAE,CAAC;YAEjB,GAAG,CAAC,IAAI,CAAC;gBACL,OAAO,EAAE,4BAA4B;gBACrC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;aAClB,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAC9C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"pulse-config.js","sourceRoot":"","sources":["../../src/routes/pulse-config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulsewatch-server",
3
- "version": "1.0.61",
3
+ "version": "1.0.62",
4
4
  "description": "Complete uptime monitoring server - Drop-in Express backend with monitoring engine, database, REST API, and real-time updates. No authentication required.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -87,4 +87,4 @@
87
87
  "engines": {
88
88
  "node": ">=20.0.0"
89
89
  }
90
- }
90
+ }