jxp 2.16.0 → 3.0.0

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.
@@ -19,11 +19,16 @@
19
19
  "-u",
20
20
  "tdd",
21
21
  "--timeout",
22
- "999999",
22
+ "30000",
23
23
  "--colors",
24
+ "--exit",
25
+ "--bail",
24
26
  "${workspaceFolder}/test"
25
27
  ],
26
- "internalConsoleOptions": "openOnSessionStart"
28
+ "internalConsoleOptions": "openOnSessionStart",
29
+ "env": {
30
+ "NODE_ENV": "test"
31
+ }
27
32
  }
28
33
  ]
29
34
  }
package/bin/server.js CHANGED
@@ -26,9 +26,9 @@ config.callbacks = {
26
26
  // console.log("Delete callback");
27
27
  // }
28
28
 
29
- post: function() {},
30
- put: function() {},
31
- delete: function() {}
29
+ post: function () { },
30
+ put: function () { },
31
+ delete: function () { }
32
32
  };
33
33
 
34
34
  config.pre_hooks = {
@@ -56,7 +56,19 @@ config.pre_hooks = {
56
56
  mongoose.Promise = Promise;
57
57
  if (!config.mongo) config.mongo = {};
58
58
  if (!config.mongo.options) config.mongo.options = {};
59
- const mongo_options = Object.assign(config.mongo.options, {});
59
+ const mongo_options = Object.assign(config.mongo.options, {
60
+ useNewUrlParser: true,
61
+ useUnifiedTopology: true,
62
+ serverSelectionTimeoutMS: 10000,
63
+ socketTimeoutMS: 45000,
64
+ maxPoolSize: process.env.NODE_ENV === 'test' ? 20 : 50,
65
+ minPoolSize: process.env.NODE_ENV === 'test' ? 5 : 10,
66
+ maxIdleTimeMS: 30000,
67
+ connectTimeoutMS: 10000,
68
+ heartbeatFrequencyMS: 10000,
69
+ retryWrites: true,
70
+ retryReads: true
71
+ });
60
72
 
61
73
  const connection_string = require("../libs/connection_string");
62
74
  console.log(`Connecting to ${connection_string}`);
@@ -65,18 +77,53 @@ mongoose.connect(connection_string, mongo_options);
65
77
  const db = mongoose.connection;
66
78
 
67
79
  // mongodb error
68
- db.on('error', console.error.bind(console, 'connection error:'));
80
+ db.on('error', (err) => {
81
+ console.error('MongoDB connection error:', err);
82
+ if (err.name === 'MongoNetworkError') {
83
+ // Handle network errors
84
+ console.error('Network error occurred. Attempting to reconnect...');
85
+ }
86
+ });
69
87
 
70
88
  // mongodb connection open
71
89
  db.once('open', () => {
72
90
  console.log(`Connected to Mongo at: ${new Date()}`);
91
+ console.log('Connection pool size:', mongoose.connection.base.connections.length);
73
92
  });
74
93
 
94
+ mongoose.connection.on('connected', () => {
95
+ console.log('Mongoose connected');
96
+ console.log('Connection pool size:', mongoose.connection.base.connections.length);
97
+ });
98
+
99
+ mongoose.connection.on('disconnected', () => {
100
+ console.log('Mongoose disconnected');
101
+ // Attempt to reconnect after a delay
102
+ setTimeout(() => {
103
+ mongoose.connect(connection_string, mongo_options).catch(err => {
104
+ console.error('Failed to reconnect to MongoDB:', err);
105
+ });
106
+ }, 5000);
107
+ });
108
+
109
+ mongoose.connection.on('error', (err) => {
110
+ console.error('Mongoose connection error:', err);
111
+ });
112
+
113
+ // Monitor connection pool
114
+ setInterval(() => {
115
+ const poolSize = mongoose.connection.base.connections.length;
116
+ const maxPoolSize = mongo_options.maxPoolSize;
117
+ if (poolSize > maxPoolSize * 0.8) {
118
+ console.warn(`Connection pool is at ${Math.round((poolSize / maxPoolSize) * 100)}% capacity`);
119
+ }
120
+ }, 30000);
121
+
75
122
  var server = new JXP(config);
76
123
 
77
124
  let port = process.env.NODE_DOCKER_PORT || process.env.PORT || config.port || 4001;
78
125
  if (process.env.NODE_ENV === "test") port = 4005;
79
- server.listen(port, function() {
126
+ server.listen(port, function () {
80
127
  console.log('%s listening at %s', `${pkg.name} v${pkg.version}`, server.url);
81
128
  console.log(`Mongoose version ${mongoose.version}`);
82
129
  });
@@ -179,5 +179,5 @@ Header: `Authorization: Bearer <your bearer token>`
179
179
 
180
180
  ### API Key
181
181
 
182
- The API Key is a permanent key that doesn't expire. It can be used by adding `?apikey=<apikey>` to the end of any request, or sending `apikey: <apikey>` in the header.
182
+ The API Key is a permanent key that doesn't expire. It can be used by adding `?apikey=<apikey>` to the end of any request, or sending `x-api-key: <apikey>` in the header.
183
183
 
package/libs/login.js CHANGED
@@ -65,7 +65,7 @@ const logout = async (req, res) => {
65
65
  if (!res.user) throw new errors.ForbiddenError("You don't seem to be logged in");
66
66
  await security.revokeToken(res.user._id);
67
67
  res.send({ status: "ok", message: "User logged out" });
68
- } catch(err) {
68
+ } catch (err) {
69
69
  console.error(err);
70
70
  if (err.code) throw err;
71
71
  throw new errors.InternalServerError(err.toString());
@@ -148,13 +148,17 @@ const login = async (req, res) => {
148
148
  }
149
149
  if ((!password) || (!email)) {
150
150
  console.error(new Date(), "Missing email or password parameters");
151
- return new errors.ForbiddenError("Missing email or password parameters");
151
+ throw new errors.ForbiddenError("Missing email or password parameters");
152
152
  }
153
153
  try {
154
154
  const user = await User.findOne({ email });
155
- if (!user) throw (`Incorrect email; email: ${email}`);
155
+ if (!user) {
156
+ console.error(new Date(), `Authentication failed - user not found`, ip, email);
157
+ throw new errors.ForbiddenError("Incorrect email or password");
158
+ }
156
159
  if (!(await bcrypt.compare(password, user.password))) {
157
- throw (`Incorrect password; email: ${email}`);
160
+ console.error(new Date(), `Authentication failed - invalid password`, ip, email);
161
+ throw new errors.ForbiddenError("Incorrect email or password");
158
162
  }
159
163
  const token = await security.refreshToken(user._id);
160
164
  const refreshtoken = await security.ensureRefreshToken(user._id);
@@ -171,7 +175,7 @@ const login = async (req, res) => {
171
175
  } catch (err) {
172
176
  console.error(new Date(), `Authentication failed`, ip, err);
173
177
  if (err.code) throw err;
174
- return new errors.ForbiddenError(err.toString());
178
+ throw new errors.ForbiddenError("Incorrect email or password");
175
179
  }
176
180
  }
177
181
 
package/libs/security.js CHANGED
@@ -9,7 +9,7 @@ var User = null;
9
9
  var RefreshToken = null;
10
10
  var provider = "https://api.workspaceman.nl";
11
11
 
12
- const init = function(config) {
12
+ const init = function (config) {
13
13
  APIKey = require(path.join(config.model_dir, "apikey_model"));
14
14
  Groups = require(path.join(config.model_dir, "usergroups_model.js"));
15
15
  User = require(path.join(config.model_dir, "user_model"));
@@ -18,7 +18,7 @@ const init = function(config) {
18
18
  if (config.url) provider = config.url;
19
19
  };
20
20
 
21
- const basicAuthData = function(req) {
21
+ const basicAuthData = function (req) {
22
22
  if (!req.headers.authorization) {
23
23
  return false;
24
24
  }
@@ -34,19 +34,19 @@ const basicAuthData = function(req) {
34
34
  const basicAuth = async ba => {
35
35
  try {
36
36
  if (!Array.isArray(ba) || ba.length !== 2) {
37
- throw("Basic Auth incorrectly formatted");
37
+ throw ("Basic Auth incorrectly formatted");
38
38
  }
39
39
  var email = ba[0];
40
40
  var password = ba[1];
41
41
  const user = await User.findOne({ email }).exec();
42
42
  if (!user) {
43
- throw(new Date(), `Incorrect username or password for ${email}`);
43
+ throw (new Date(), `Incorrect username or password for ${email}`);
44
44
  }
45
45
  if (!await bcrypt.compare(password, user.password)) {
46
- throw(`Incorrect username or password for ${email}`);
46
+ throw (`Incorrect username or password for ${email}`);
47
47
  }
48
48
  return user;
49
- } catch(err) {
49
+ } catch (err) {
50
50
  console.error(new Date(), err);
51
51
  throw err;
52
52
  }
@@ -66,13 +66,13 @@ const bearerAuthData = req => {
66
66
 
67
67
  const bearerAuth = async t => {
68
68
  try {
69
- if (!t) throw("Token invalid");
69
+ if (!t) throw ("Token invalid");
70
70
  const token = await Token.findOne({ access_token: t, provider }).exec();
71
71
  if (!token) {
72
- throw(`Token ${t} not found`);
72
+ throw (`Token ${t} not found`);
73
73
  }
74
74
  if (!tokenIsValid(token)) {
75
- throw(`Token is no loger valid`);
75
+ throw (`Token is no loger valid`);
76
76
  }
77
77
  const user = await User.findOne({ _id: token.user_id }).exec();
78
78
  if (!user) {
@@ -87,13 +87,13 @@ const bearerAuth = async t => {
87
87
 
88
88
  const apiKeyAuth = async apikey => {
89
89
  try {
90
- if (!apikey) throw("Missing apikey");
90
+ if (!apikey) throw ("Missing apikey");
91
91
  const result = await APIKey.findOne({ apikey });
92
- if (!result) throw("Could not find apikey");
92
+ if (!result) throw ("Could not find apikey");
93
93
  const user = User.findOne({ _id: result.user_id });
94
- if (!user) throw("Could not find user associated to apikey");
94
+ if (!user) throw ("Could not find user associated to apikey");
95
95
  return user;
96
- } catch(err) {
96
+ } catch (err) {
97
97
  console.error(new Date(), err);
98
98
  throw err;
99
99
  }
@@ -104,7 +104,7 @@ const getGroups = async user_id => {
104
104
  const userGroup = await Groups.findOne({ user_id });
105
105
  var groups = userGroup && userGroup.groups ? userGroup.groups : [];
106
106
  return groups;
107
- } catch(err) {
107
+ } catch (err) {
108
108
  console.error(new Date(), err);
109
109
  throw err;
110
110
  }
@@ -126,7 +126,7 @@ const generateApiKey = async user_id => {
126
126
  apikey.apikey = randToken.generate(16);
127
127
  await apikey.save();
128
128
  return apikey;
129
- } catch(err) {
129
+ } catch (err) {
130
130
  console.error(new Date(), err);
131
131
  throw err;
132
132
  }
@@ -208,10 +208,10 @@ const revokeRefreshToken = async user_id => {
208
208
 
209
209
  const refresh = async (req, res) => {
210
210
  try {
211
- if(req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("bearer") === 0) {
211
+ if (req.headers.authorization && req.headers.authorization.trim().toLowerCase().indexOf("bearer") === 0) {
212
212
  const refresh_token = await RefreshToken.findOne({ refresh_token: bearerAuthData(req) }).exec();
213
- if (!refresh_token) throw("Refresh token not found");
214
- if (!tokenIsValid(refresh_token)) throw("Refresh token has expired");
213
+ if (!refresh_token) throw ("Refresh token not found");
214
+ if (!tokenIsValid(refresh_token)) throw ("Refresh token has expired");
215
215
  const user_id = refresh_token.user_id;
216
216
  const token = await refreshToken(user_id);
217
217
  await revokeRefreshToken(user_id);
@@ -224,7 +224,7 @@ const refresh = async (req, res) => {
224
224
  refresh_token_expires: tokenExpires(new_refresh_token)
225
225
  });
226
226
  } else {
227
- throw("Missing refresh token")
227
+ throw ("Missing refresh token")
228
228
  }
229
229
  } catch (err) {
230
230
  console.error(err);
@@ -242,7 +242,7 @@ const login = async (req, res) => {
242
242
  return;
243
243
  }
244
244
  res = Object.assign(res, authenticate_result);
245
- } catch(err) {
245
+ } catch (err) {
246
246
  console.error(err);
247
247
  if (err.code) throw err;
248
248
  throw new errors.ForbiddenError(err.toString());
@@ -264,7 +264,7 @@ const authenticate = async req => {
264
264
  user = await apiKeyAuth(req.query.apikey);
265
265
  } else if (req.headers["X-API-Key"] || req.headers["x-api-key"]) {
266
266
  // API Key
267
- user = await apiKeyAuth(req.query.apikey || req.headers["X-API-Key"] || req.headers["x-api-key"])
267
+ user = await apiKeyAuth(req.headers["X-API-Key"] || req.headers["x-api-key"])
268
268
  } else {
269
269
  throw ("Could not find any way to authenticate");
270
270
  }
@@ -278,7 +278,7 @@ const authenticate = async req => {
278
278
  username: user.email,
279
279
  user
280
280
  }
281
-
281
+
282
282
  }
283
283
 
284
284
  const auth = async (req, res) => {
@@ -303,7 +303,7 @@ const auth = async (req, res) => {
303
303
  throw new errors.InternalServerError(`Unsupported operation: ${req.method}`);
304
304
  }
305
305
  return await check_perms(res.user, res.groups, req.Model, method, req.params.item_id);
306
- } catch(err) {
306
+ } catch (err) {
307
307
  console.error(err);
308
308
  if (err.code) throw err;
309
309
  throw new errors.ForbiddenError(err.toString());
@@ -355,7 +355,7 @@ const check_perms = async (user, groups, model, method, item_id) => {
355
355
  }
356
356
  //Group check
357
357
  for (let group of groups) {
358
- if (perms[group] && perms[group].includes(method) ) {
358
+ if (perms[group] && perms[group].includes(method)) {
359
359
  // console.log("Matched permission '" + group + "':" + method);
360
360
  return;
361
361
  }
package/libs/ws.js CHANGED
@@ -235,24 +235,47 @@ class WSClient {
235
235
  wss.on('connection', function connection(ws) {
236
236
  try {
237
237
  const client = new WSClient(ws);
238
- // clients[client.id] = client;
238
+ const startTime = Date.now();
239
+ console.log(`New WebSocket connection established at ${new Date()}`);
240
+
239
241
  ws.on('message', async function message(msg) {
242
+ const messageStartTime = Date.now();
240
243
  try {
241
- console.log(`Received message from ${client.id}`);
244
+ console.log(`Received message from ${client.id} at ${new Date()}`);
242
245
  const result = await client.receive(msg);
243
246
  ws.send(JSON.stringify(result));
247
+ console.log(`Message processed in ${Date.now() - messageStartTime}ms`);
244
248
  } catch (err) {
245
249
  console.error("message error", err);
250
+ console.error(`Message processing failed after ${Date.now() - messageStartTime}ms`);
246
251
  }
247
252
  });
253
+
248
254
  ws.on('close', function close() {
255
+ const connectionDuration = Date.now() - startTime;
249
256
  if (client.user) {
250
- console.log(`Close ${client.user.name} <${client.user.email}>`);
257
+ console.log(`Connection closed for ${client.user.name} <${client.user.email}> after ${connectionDuration}ms`);
251
258
  } else {
252
- console.log(`Close anonymous connection`);
259
+ console.log(`Anonymous connection closed after ${connectionDuration}ms`);
253
260
  }
254
261
  client.close();
255
262
  });
263
+
264
+ // Add ping/pong monitoring
265
+ const pingInterval = setInterval(() => {
266
+ if (ws.readyState === ws.OPEN) {
267
+ const pingStart = Date.now();
268
+ ws.ping();
269
+ ws.once('pong', () => {
270
+ const latency = Date.now() - pingStart;
271
+ if (latency > 1000) { // Alert if latency > 1 second
272
+ console.warn(`High WebSocket latency detected: ${latency}ms`);
273
+ }
274
+ });
275
+ }
276
+ }, 30000);
277
+
278
+ ws.on('close', () => clearInterval(pingInterval));
256
279
  } catch (err) {
257
280
  console.error("connection error", err);
258
281
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jxp",
3
3
  "description:": "An opinionated RESTful API library based on Mongoose and Restify. Make an API by just writing Mongoose models.",
4
- "version": "2.16.0",
4
+ "version": "3.0.0",
5
5
  "private": false,
6
6
  "main": "libs/jxp.js",
7
7
  "scripts": {
@@ -33,7 +33,7 @@
33
33
  "bcryptjs": "^3.0.2",
34
34
  "commander": "^13.1.0",
35
35
  "config": "^3.3.12",
36
- "dotenv": "^16.4.7",
36
+ "dotenv": "^16.5.0",
37
37
  "glob": "11.0.1",
38
38
  "js-yaml": "4.1.0",
39
39
  "json2csv": "^5.0.7",
@@ -46,7 +46,7 @@
46
46
  "mongoose-friendly": "^0.1.4",
47
47
  "morgan": "^1.10.0",
48
48
  "node-cache": "^5.1.2",
49
- "nodemailer": "^6.10.0",
49
+ "nodemailer": "^6.10.1",
50
50
  "nodemailer-smtp-transport": "^2.7.4",
51
51
  "path": "^0.12.7",
52
52
  "pug": "^3.0.3",
package/test/auth.js ADDED
@@ -0,0 +1,190 @@
1
+ process.env.NODE_ENV = 'test';
2
+
3
+ var chai = require('chai');
4
+ var chaiHttp = require('chai-http');
5
+
6
+ var init = require("./init");
7
+ var server = require("../bin/server");
8
+
9
+ chai.use(chaiHttp);
10
+
11
+ describe('Authentication Tests', () => {
12
+ before(async function () {
13
+ // First empty collections
14
+ await init.empty_user_collections();
15
+ console.log("Emptied collections");
16
+
17
+ // Then run setup
18
+ await new Promise((resolve, reject) => {
19
+ chai.request(server)
20
+ .post("/setup")
21
+ .send({ email: init.email, password: init.password })
22
+ .end((err, res) => {
23
+ if (err) return reject(err);
24
+ res.should.have.status(200);
25
+ res.body.status.should.equal('success');
26
+ console.log("Setup complete with email:", init.email);
27
+ resolve();
28
+ });
29
+ });
30
+ });
31
+
32
+ var apikey = null;
33
+ var token = null;
34
+ var refresh_token = null;
35
+ var user_id = null;
36
+
37
+ describe("Login", () => {
38
+ it("should login with valid credentials", (done) => {
39
+ chai.request(server)
40
+ .post("/login")
41
+ .send({ email: init.email, password: init.password })
42
+ .end((err, res) => {
43
+ res.should.have.status(200);
44
+ res.body.should.have.property('user_id');
45
+ res.body.should.have.property('apikey');
46
+ res.body.should.have.property('token');
47
+ res.body.should.have.property('token_expires');
48
+ res.body.should.have.property('refresh_token');
49
+ res.body.should.have.property('refresh_token_expires');
50
+ res.body.should.have.property('provider');
51
+ apikey = res.body.apikey;
52
+ token = res.body.token;
53
+ user_id = res.body.user_id;
54
+ refresh_token = res.body.refresh_token;
55
+ done();
56
+ });
57
+ });
58
+
59
+ it("should fail with invalid credentials", (done) => {
60
+ console.log("Attempting login with invalid password for:", init.email);
61
+ chai.request(server)
62
+ .post("/login")
63
+ .send({ email: init.email, password: "wrongpassword" })
64
+ .end((err, res) => {
65
+ if (err) return done(err);
66
+ console.log("Response status:", res.status);
67
+ console.log("Response body:", res.body);
68
+ res.should.have.status(403);
69
+ done();
70
+ });
71
+ });
72
+ });
73
+
74
+ describe("Token Authentication", () => {
75
+ it("should authenticate with valid token", (done) => {
76
+ chai.request(server)
77
+ .get("/api/user")
78
+ .set("Authorization", `Bearer ${token}`)
79
+ .end((err, res) => {
80
+ if (err) return done(err);
81
+ res.should.have.status(200);
82
+ res.body.data.should.be.an('array');
83
+ done();
84
+ });
85
+ });
86
+
87
+ it("should fail with invalid token", (done) => {
88
+ chai.request(server)
89
+ .get("/api/user")
90
+ .set("Authorization", "Bearer invalidtoken")
91
+ .end((err, res) => {
92
+ if (err) return done(err);
93
+ res.should.have.status(403);
94
+ done();
95
+ });
96
+ });
97
+ });
98
+
99
+ describe("API Key Authentication", () => {
100
+ it("should authenticate with valid API key in header", (done) => {
101
+ chai.request(server)
102
+ .get("/api/user")
103
+ .set("X-API-Key", apikey)
104
+ .end((err, res) => {
105
+ if (err) return done(err);
106
+ res.should.have.status(200);
107
+ res.body.data.should.be.an('array');
108
+ done();
109
+ });
110
+ });
111
+
112
+ it("should authenticate with valid API key in query", (done) => {
113
+ chai.request(server)
114
+ .get(`/api/user?apikey=${apikey}`)
115
+ .end((err, res) => {
116
+ if (err) return done(err);
117
+ res.should.have.status(200);
118
+ res.body.data.should.be.an('array');
119
+ done();
120
+ });
121
+ });
122
+
123
+ it("should fail with invalid API key", (done) => {
124
+ chai.request(server)
125
+ .get("/api/user")
126
+ .set("X-API-Key", "invalidapikey")
127
+ .end((err, res) => {
128
+ if (err) return done(err);
129
+ res.should.have.status(403);
130
+ done();
131
+ });
132
+ });
133
+ });
134
+
135
+ describe("Refresh Token", () => {
136
+ it("should refresh token successfully", (done) => {
137
+ chai.request(server)
138
+ .post("/refresh")
139
+ .set("Authorization", `Bearer ${refresh_token}`)
140
+ .end((err, res) => {
141
+ if (err) return done(err);
142
+ res.should.have.status(200);
143
+ res.body.should.have.property('token');
144
+ res.body.should.have.property('token_expires');
145
+ res.body.should.have.property('refresh_token');
146
+ res.body.should.have.property('refresh_token_expires');
147
+ res.body.token.should.not.eql(token);
148
+ res.body.refresh_token.should.not.eql(refresh_token);
149
+ token = res.body.token;
150
+ refresh_token = res.body.refresh_token;
151
+ done();
152
+ });
153
+ });
154
+
155
+ it("should fail with invalid refresh token", (done) => {
156
+ chai.request(server)
157
+ .post("/refresh")
158
+ .set("Authorization", "Bearer invalidrefreshtoken")
159
+ .end((err, res) => {
160
+ if (err) return done(err);
161
+ res.should.have.status(403);
162
+ done();
163
+ });
164
+ });
165
+ });
166
+
167
+ describe("Logout", () => {
168
+ it("should logout successfully", (done) => {
169
+ chai.request(server)
170
+ .get("/login/logout")
171
+ .set("Authorization", `Bearer ${token}`)
172
+ .end((err, res) => {
173
+ if (err) return done(err);
174
+ res.should.have.status(200);
175
+ done();
176
+ });
177
+ });
178
+
179
+ it("should not be able to use token after logout", (done) => {
180
+ chai.request(server)
181
+ .get("/api/user")
182
+ .set("Authorization", `Bearer ${token}`)
183
+ .end((err, res) => {
184
+ if (err) return done(err);
185
+ res.should.have.status(403);
186
+ done();
187
+ });
188
+ });
189
+ });
190
+ });
package/test/init.js CHANGED
@@ -15,9 +15,9 @@ const security = require("../libs/security");
15
15
  const empty = async model => {
16
16
  try {
17
17
  await model.deleteMany({});
18
- } catch(err) {
18
+ } catch (err) {
19
19
  console.error(err);
20
- throw(err);
20
+ throw (err);
21
21
  }
22
22
  };
23
23
 
@@ -44,9 +44,9 @@ const init = async () => {
44
44
  await post(User, { name: "Admin User", email: admin_email, password: security.encPassword(admin_password), urlid: "admin-user", admin: true });
45
45
  await post(User, { name: "Test User", email, password: security.encPassword(password), urlid: "test-user" });
46
46
  return true;
47
- } catch(err) {
47
+ } catch (err) {
48
48
  console.error(err);
49
- throw(err);
49
+ throw (err);
50
50
  }
51
51
  };
52
52
 
@@ -55,9 +55,9 @@ const empty_user_collections = async () => {
55
55
  await empty(User);
56
56
  await empty(Apikey);
57
57
  await empty(Test);
58
- } catch(err) {
58
+ } catch (err) {
59
59
  console.error(err);
60
- throw(err);
60
+ throw (err);
61
61
  }
62
62
  }
63
63