@loomcore/api 0.2.6 → 0.2.8

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 (77) hide show
  1. package/dist/__tests__/common-test.utils.d.ts +5 -5
  2. package/dist/__tests__/common-test.utils.js +24 -21
  3. package/dist/controllers/auth.controller.d.ts +9 -7
  4. package/dist/controllers/auth.controller.js +69 -27
  5. package/dist/controllers/authorizations.controller.d.ts +3 -3
  6. package/dist/controllers/authorizations.controller.js +4 -4
  7. package/dist/controllers/roles.controller.d.ts +1 -1
  8. package/dist/controllers/roles.controller.js +3 -3
  9. package/dist/controllers/users.controller.d.ts +5 -6
  10. package/dist/controllers/users.controller.js +4 -15
  11. package/dist/databases/postgres/commands/postgres-create.command.d.ts +2 -2
  12. package/dist/databases/postgres/commands/postgres-create.command.js +5 -5
  13. package/dist/databases/postgres/postgres.database.d.ts +2 -2
  14. package/dist/databases/postgres/postgres.database.js +6 -8
  15. package/dist/middleware/is-authorized.d.ts +2 -2
  16. package/dist/middleware/is-authorized.js +13 -11
  17. package/dist/models/refresh-token.model.d.ts +1 -1
  18. package/dist/models/refresh-token.model.js +3 -4
  19. package/dist/services/generic-api-service/generic-api-service.interface.d.ts +6 -6
  20. package/dist/services/generic-api-service/generic-api.service.d.ts +8 -8
  21. package/dist/services/generic-api-service/generic-api.service.js +16 -16
  22. package/dist/services/index.d.ts +11 -12
  23. package/dist/services/index.js +11 -12
  24. package/dist/services/multi-tenant-api.service.js +6 -2
  25. package/dist/services/password-reset-token.service.d.ts +2 -3
  26. package/dist/services/password-reset-token.service.js +4 -9
  27. package/dist/services/refresh-token.service.d.ts +6 -0
  28. package/dist/services/refresh-token.service.js +7 -0
  29. package/dist/services/user.service.js +4 -4
  30. package/dist/utils/auth/attempt-login.util.d.ts +4 -0
  31. package/dist/utils/auth/attempt-login.util.js +47 -0
  32. package/dist/utils/auth/change-password.util.d.ts +4 -0
  33. package/dist/utils/auth/change-password.util.js +14 -0
  34. package/dist/utils/auth/create-new-refresh-token.util.d.ts +4 -0
  35. package/dist/utils/auth/create-new-refresh-token.util.js +29 -0
  36. package/dist/utils/auth/create-new-tokens.util.d.ts +3 -0
  37. package/dist/utils/auth/create-new-tokens.util.js +13 -0
  38. package/dist/utils/auth/generate-device-id.util.d.ts +1 -0
  39. package/dist/utils/auth/generate-device-id.util.js +4 -0
  40. package/dist/utils/auth/generate-refresh-token.util.d.ts +1 -0
  41. package/dist/utils/auth/generate-refresh-token.util.js +4 -0
  42. package/dist/utils/auth/get-active-refresh-token.util.d.ts +4 -0
  43. package/dist/utils/auth/get-active-refresh-token.util.js +19 -0
  44. package/dist/utils/auth/get-and-set-device-id-cookie.util.d.ts +2 -0
  45. package/dist/utils/auth/get-and-set-device-id-cookie.util.js +23 -0
  46. package/dist/utils/auth/get-auth-config.util.d.ts +2 -0
  47. package/dist/utils/auth/get-auth-config.util.js +8 -0
  48. package/dist/utils/auth/get-device-id-from-cookie.util.d.ts +2 -0
  49. package/dist/utils/auth/get-device-id-from-cookie.util.js +3 -0
  50. package/dist/utils/auth/get-expires-on-from-days.util.d.ts +1 -0
  51. package/dist/utils/auth/get-expires-on-from-days.util.js +3 -0
  52. package/dist/utils/auth/get-expires-on-from-minutes.util.d.ts +1 -0
  53. package/dist/utils/auth/get-expires-on-from-minutes.util.js +3 -0
  54. package/dist/utils/auth/get-expires-on-from-seconds.util.d.ts +1 -0
  55. package/dist/utils/auth/get-expires-on-from-seconds.util.js +3 -0
  56. package/dist/utils/auth/index.d.ts +18 -0
  57. package/dist/utils/auth/index.js +18 -0
  58. package/dist/utils/auth/log-user-in.util.d.ts +3 -0
  59. package/dist/utils/auth/log-user-in.util.js +18 -0
  60. package/dist/utils/auth/request-token-using-refresh-token.util.d.ts +3 -0
  61. package/dist/utils/auth/request-token-using-refresh-token.util.js +9 -0
  62. package/dist/utils/auth/reset-password.util.d.ts +4 -0
  63. package/dist/utils/auth/reset-password.util.js +50 -0
  64. package/dist/utils/auth/send-reset-password-email.util.d.ts +3 -0
  65. package/dist/utils/auth/send-reset-password-email.util.js +30 -0
  66. package/dist/utils/auth/update-last-logged-in.util.d.ts +3 -0
  67. package/dist/utils/auth/update-last-logged-in.util.js +15 -0
  68. package/dist/utils/index.d.ts +8 -6
  69. package/dist/utils/index.js +8 -6
  70. package/dist/utils/jwt.utils.d.ts +2 -0
  71. package/dist/utils/jwt.utils.js +12 -0
  72. package/dist/utils/password.utils.js +8 -8
  73. package/package.json +3 -3
  74. package/dist/services/auth.service.d.ts +0 -49
  75. package/dist/services/auth.service.js +0 -277
  76. package/dist/services/jwt.service.d.ts +0 -4
  77. package/dist/services/jwt.service.js +0 -18
@@ -1,15 +1,15 @@
1
- import { IQueryOptions, IUser, IUserContext } from "@loomcore/common/models";
2
- import { Application, NextFunction, Request, Response } from "express";
1
+ import { type IQueryOptions, type IUser, type IUserContext } from "@loomcore/common/models";
2
+ import type { Application, NextFunction, Request, Response } from "express";
3
3
  import { ApiController } from "../controllers/api.controller.js";
4
- import { IDatabase } from "../databases/models/index.js";
5
- import { Operation } from "../databases/operations/operation.js";
4
+ import type { IDatabase } from "../databases/models/index.js";
5
+ import type { Operation } from "../databases/operations/operation.js";
6
6
  import { GenericApiService } from "../services/index.js";
7
7
  import { MultiTenantApiService } from "../services/multi-tenant-api.service.js";
8
8
  import type { DbType } from "../databases/db-type.type.js";
9
9
  import { type ICategory } from "./models/category.model.js";
10
10
  import { type IProduct } from "./models/product.model.js";
11
11
  import { type IProductWithCategory } from "./models/product-with-category.model.js";
12
- declare function initialize(database: IDatabase): void;
12
+ declare function initialize(db: IDatabase): void;
13
13
  declare function getRandomId(): string;
14
14
  declare function isMongoDatabase(database: IDatabase): boolean;
15
15
  declare function isPostgresDatabase(database: IDatabase): boolean;
@@ -1,21 +1,21 @@
1
+ import crypto from "node:crypto";
1
2
  import { EmptyUserContext, } from "@loomcore/common/models";
2
3
  import { Type } from "@sinclair/typebox";
3
4
  import { Value } from "@sinclair/typebox/value";
4
- import crypto from "crypto";
5
5
  import jwt from "jsonwebtoken";
6
6
  import { ObjectId } from "mongodb";
7
7
  import { ApiController } from "../controllers/api.controller.js";
8
8
  import { MongoDBDatabase } from "../databases/mongo-db/mongo-db.database.js";
9
9
  import { LeftJoin } from "../databases/operations/left-join.operation.js";
10
10
  import { PostgresDatabase } from "../databases/postgres/postgres.database.js";
11
- import { AuthService, GenericApiService } from "../services/index.js";
12
- import { JwtService } from "../services/jwt.service.js";
11
+ import { GenericApiService, UserService } from "../services/index.js";
13
12
  import { MultiTenantApiService } from "../services/multi-tenant-api.service.js";
14
13
  import { OrganizationService } from "../services/organization.service.js";
15
14
  import * as testObjectsModule from "./test-objects.js";
16
15
  const { getTestMetaOrg, getTestOrg, getTestMetaOrgUser, getTestMetaOrgUserContext, getTestOrgUserContext, setTestOrgId, setTestMetaOrgId, setTestMetaOrgUserId, setTestOrgUserId, } = testObjectsModule;
17
16
  import { entityUtils } from "@loomcore/common/utils";
18
17
  import { config, setBaseApiConfig } from "../config/index.js";
18
+ import { attemptLogin } from "../utils/auth/index.js";
19
19
  import { apiUtils } from "../utils/index.js";
20
20
  import { CategorySpec } from "./models/category.model.js";
21
21
  import { ProductSpec } from "./models/product.model.js";
@@ -23,15 +23,17 @@ import { ProductWithCategoryPublicSpec, ProductWithCategorySpec, } from "./model
23
23
  import { TestEmailClient } from "./test-email-client.js";
24
24
  import { getTestOrgUser } from "./test-objects.js";
25
25
  let deviceIdCookie;
26
- let authService;
26
+ let database;
27
27
  let organizationService;
28
+ let userService;
28
29
  const JWT_SECRET = "test-secret";
29
30
  const newUser1Email = "one@test.com";
30
31
  const newUser1Password = "testone";
31
32
  const constDeviceIdCookie = crypto.randomBytes(16).toString("hex");
32
- function initialize(database) {
33
- authService = new AuthService(database);
34
- organizationService = new OrganizationService(database);
33
+ function initialize(db) {
34
+ database = db;
35
+ organizationService = new OrganizationService(db);
36
+ userService = new UserService(db);
35
37
  deviceIdCookie = constDeviceIdCookie;
36
38
  }
37
39
  function getRandomId() {
@@ -95,7 +97,7 @@ async function setupTestUsers() {
95
97
  }
96
98
  }
97
99
  async function createTestUsers() {
98
- if (!authService || !organizationService) {
100
+ if (!organizationService || !userService) {
99
101
  throw new Error("Database not initialized. Call initialize() first.");
100
102
  }
101
103
  try {
@@ -115,8 +117,8 @@ async function createTestUsers() {
115
117
  else {
116
118
  setTestOrgId(existingTestOrg._id);
117
119
  }
118
- const createdTestOrgUser = await authService.createUser(getTestOrgUserContext(), getTestOrgUser());
119
- const createdMetaOrgUser = await authService.createUser(getTestMetaOrgUserContext(), getTestMetaOrgUser());
120
+ const createdTestOrgUser = await userService.create(getTestOrgUserContext(), getTestOrgUser());
121
+ const createdMetaOrgUser = await userService.create(getTestMetaOrgUserContext(), getTestMetaOrgUser());
120
122
  if (!createdTestOrgUser || !createdMetaOrgUser) {
121
123
  throw new Error("Failed to create test user");
122
124
  }
@@ -130,18 +132,18 @@ async function createTestUsers() {
130
132
  }
131
133
  }
132
134
  async function deleteTestUser() {
133
- if (!authService || !organizationService) {
135
+ if (!organizationService || !userService) {
134
136
  return;
135
137
  }
136
- await authService
138
+ await userService
137
139
  .deleteById(getTestMetaOrgUserContext(), getTestMetaOrgUser()._id)
138
140
  .catch((error) => {
139
- return null;
141
+ console.log("Error deleting test user:", error);
140
142
  });
141
143
  await organizationService
142
144
  .deleteById(getTestMetaOrgUserContext(), getTestOrg()._id)
143
145
  .catch((error) => {
144
- return null;
146
+ console.log("Error deleting test organization:", error);
145
147
  });
146
148
  }
147
149
  async function simulateloginWithTestUser() {
@@ -149,20 +151,20 @@ async function simulateloginWithTestUser() {
149
151
  cookies: {},
150
152
  };
151
153
  if (deviceIdCookie) {
152
- req.cookies["deviceId"] = deviceIdCookie;
154
+ req.cookies.deviceId = deviceIdCookie;
153
155
  }
154
156
  const res = {
155
- cookie: function (name, value) {
157
+ cookie: (name, value) => {
156
158
  if (name === "deviceId") {
157
159
  deviceIdCookie = value;
158
160
  }
159
161
  return res;
160
162
  },
161
163
  };
162
- if (!authService) {
163
- throw new Error("AuthService not initialized. Call initialize() first.");
164
+ if (!database) {
165
+ throw new Error("Database not initialized. Call initialize() first.");
164
166
  }
165
- const loginResponse = await authService.attemptLogin(req, res, getTestMetaOrgUser().email, testObjectsModule.TEST_META_ORG_USER_PASSWORD);
167
+ const loginResponse = await attemptLogin(database, getTestMetaOrgUser().email, testObjectsModule.TEST_META_ORG_USER_PASSWORD, deviceIdCookie, getTestMetaOrg()._id);
166
168
  if (!loginResponse?.tokens?.accessToken) {
167
169
  throw new Error("Failed to login with test user");
168
170
  }
@@ -170,11 +172,11 @@ async function simulateloginWithTestUser() {
170
172
  }
171
173
  function getAuthToken() {
172
174
  const userContext = getTestMetaOrgUserContext();
173
- const token = JwtService.sign(userContext, JWT_SECRET, { expiresIn: 3600 });
175
+ const token = jwt.sign(userContext, JWT_SECRET, { expiresIn: 3600 });
174
176
  return `Bearer ${token}`;
175
177
  }
176
178
  function verifyToken(token) {
177
- return JwtService.verify(token, JWT_SECRET);
179
+ return jwt.verify(token, JWT_SECRET);
178
180
  }
179
181
  export class CategoryService extends GenericApiService {
180
182
  constructor(database) {
@@ -339,6 +341,7 @@ async function loginWithTestUser(agent) {
339
341
  const response = await agent.post("/api/auth/login").send({
340
342
  email: testUser.email,
341
343
  password: testUser.password,
344
+ organizationId: testUser._orgId,
342
345
  });
343
346
  if (!response.body?.data?.tokens?.accessToken) {
344
347
  console.error("Login failed:", response.body);
@@ -1,15 +1,17 @@
1
- import type { Application, NextFunction, Request, Response } from "express";
1
+ import type { Application, Request, Response } from "express";
2
2
  import type { IDatabase } from "../databases/models/index.js";
3
- import { AuthService } from "../services/index.js";
3
+ import { OrganizationService, UserService } from "../services/index.js";
4
4
  export declare class AuthController {
5
- authService: AuthService;
5
+ database: IDatabase;
6
+ userService: UserService;
7
+ organizationService: OrganizationService;
6
8
  constructor(app: Application, database: IDatabase);
7
9
  mapRoutes(app: Application): void;
8
- login(req: Request, res: Response, next: NextFunction): Promise<void>;
10
+ login(req: Request, res: Response): Promise<void>;
9
11
  registerUser(req: Request, res: Response): Promise<void>;
10
- requestTokenUsingRefreshToken(req: Request, res: Response, next: NextFunction): Promise<void>;
11
- getUserContext(req: Request, res: Response, next: NextFunction): Promise<void>;
12
- afterAuth(req: Request, res: Response, loginResponse: any): void;
12
+ requestTokenUsingRefreshToken(req: Request, res: Response): Promise<void>;
13
+ getUserContext(req: Request, res: Response): Promise<void>;
14
+ afterAuth(_req: Request, _res: Response, _loginResponse: any): void;
13
15
  changePassword(req: Request, res: Response): Promise<void>;
14
16
  forgotPassword(req: Request, res: Response): Promise<void>;
15
17
  resetPassword(req: Request, res: Response): Promise<void>;
@@ -1,13 +1,19 @@
1
- import { LoginResponseSpec, PublicUserContextSpec, PublicUserSpec, passwordValidator, TokenResponseSpec, UserSpec, } from "@loomcore/common/models";
1
+ import { EmptyUserContext, LoginResponseSpec, PublicUserContextSpec, PublicUserSpec, passwordValidator, TokenResponseSpec, UserSpec, } from "@loomcore/common/models";
2
2
  import { entityUtils } from "@loomcore/common/utils";
3
- import { BadRequestError, UnauthenticatedError } from "../errors/index.js";
3
+ import { config } from "../config/base-api-config.js";
4
+ import { BadRequestError, ServerError, UnauthenticatedError, } from "../errors/index.js";
4
5
  import { isAuthorized } from "../middleware/index.js";
5
- import { AuthService } from "../services/index.js";
6
+ import { OrganizationService, UserService } from "../services/index.js";
7
+ import { attemptLogin, changePassword, getAndSetDeviceIdCookie, getDeviceIdFromCookie, requestTokenUsingRefreshToken, resetPassword, sendResetPasswordEmail, } from "../utils/auth/index.js";
6
8
  import { apiUtils } from "../utils/index.js";
7
9
  export class AuthController {
8
- authService;
10
+ database;
11
+ userService;
12
+ organizationService;
9
13
  constructor(app, database) {
10
- this.authService = new AuthService(database);
14
+ this.database = database;
15
+ this.userService = new UserService(database);
16
+ this.organizationService = new OrganizationService(database);
11
17
  this.mapRoutes(app);
12
18
  }
13
19
  mapRoutes(app) {
@@ -19,10 +25,20 @@ export class AuthController {
19
25
  app.post(`/api/auth/forgot-password`, this.forgotPassword.bind(this));
20
26
  app.post(`/api/auth/reset-password`, this.resetPassword.bind(this));
21
27
  }
22
- async login(req, res, next) {
23
- const { email, password } = req.body;
28
+ async login(req, res) {
29
+ const { email, password, organizationId } = req.body;
30
+ if (!email || typeof email !== "string") {
31
+ throw new BadRequestError("Missing required fields: email is required.");
32
+ }
33
+ if (!password || typeof password !== "string") {
34
+ throw new BadRequestError("Missing required fields: password is required.");
35
+ }
36
+ if (config.app.isMultiTenant && !organizationId) {
37
+ throw new BadRequestError("Missing required fields: organizationId is required.");
38
+ }
24
39
  res.set("Content-Type", "application/json");
25
- const loginResponse = await this.authService.attemptLogin(req, res, email, password);
40
+ const deviceId = getAndSetDeviceIdCookie(req, res);
41
+ const loginResponse = await attemptLogin(this.database, email, password, deviceId, organizationId);
26
42
  apiUtils.apiResponse(res, 200, { data: loginResponse }, LoginResponseSpec);
27
43
  }
28
44
  async registerUser(req, res) {
@@ -31,30 +47,35 @@ export class AuthController {
31
47
  throw new BadRequestError("Missing required fields: userContext is required.");
32
48
  }
33
49
  const body = req.body;
34
- const validationErrors = this.authService.validate(body.user);
50
+ const validationErrors = this.userService.validate(body.user);
35
51
  entityUtils.handleValidationResult(validationErrors, "AuthController.registerUser");
36
- const user = await this.authService.createUser(userContext, body.user);
37
- apiUtils.apiResponse(res, 201, { data: user || undefined }, UserSpec, PublicUserSpec);
52
+ const user = await this.userService.create(userContext, body.user);
53
+ if (!user) {
54
+ throw new ServerError("Failed to create user");
55
+ }
56
+ apiUtils.apiResponse(res, 201, { data: user }, UserSpec, PublicUserSpec);
38
57
  }
39
- async requestTokenUsingRefreshToken(req, res, next) {
58
+ async requestTokenUsingRefreshToken(req, res) {
59
+ const userContext = req.userContext;
60
+ if (!userContext) {
61
+ throw new BadRequestError("Missing required fields: userContext is required.");
62
+ }
40
63
  const refreshToken = req.query.refreshToken;
41
64
  if (!refreshToken || typeof refreshToken !== "string") {
42
65
  throw new BadRequestError("Missing required fields: refreshToken is required.");
43
66
  }
44
- const deviceId = this.authService.getDeviceIdFromCookie(req);
45
- const tokens = await this.authService.requestTokenUsingRefreshToken(refreshToken, deviceId);
46
- if (tokens) {
47
- apiUtils.apiResponse(res, 200, { data: tokens }, TokenResponseSpec);
48
- }
49
- else {
67
+ const deviceId = getDeviceIdFromCookie(req);
68
+ const tokens = await requestTokenUsingRefreshToken(this.database, userContext, refreshToken, deviceId);
69
+ if (!tokens) {
50
70
  throw new UnauthenticatedError();
51
71
  }
72
+ apiUtils.apiResponse(res, 200, { data: tokens }, TokenResponseSpec);
52
73
  }
53
- async getUserContext(req, res, next) {
74
+ async getUserContext(req, res) {
54
75
  const userContext = req.userContext;
55
76
  apiUtils.apiResponse(res, 200, { data: userContext }, PublicUserContextSpec);
56
77
  }
57
- afterAuth(req, res, loginResponse) {
78
+ afterAuth(_req, _res, _loginResponse) {
58
79
  console.log("in afterAuth");
59
80
  }
60
81
  async changePassword(req, res) {
@@ -62,31 +83,52 @@ export class AuthController {
62
83
  if (!userContext) {
63
84
  throw new BadRequestError("Missing required fields: userContext is required.");
64
85
  }
65
- const body = req.body;
66
- const validationErrors = entityUtils.validate(UserSpec, { password: body.password }, true, passwordValidator);
86
+ const password = req.body?.password;
87
+ const validationErrors = entityUtils.validate(UserSpec, { password: password }, true, passwordValidator);
67
88
  entityUtils.handleValidationResult(validationErrors, "AuthController.changePassword");
68
- const updateResult = await this.authService.changeLoggedInUsersPassword(userContext, body);
89
+ const updateResult = await changePassword(this.database, userContext, password);
69
90
  apiUtils.apiResponse(res, 200, { data: updateResult });
70
91
  }
71
92
  async forgotPassword(req, res) {
72
93
  const email = req.body?.email;
94
+ const organizationId = req.body?.organizationId;
95
+ if (!email || typeof email !== "string") {
96
+ throw new BadRequestError("Missing required fields: email is required.");
97
+ }
98
+ if (config.app.isMultiTenant && !organizationId) {
99
+ throw new BadRequestError("Missing required fields: organizationId is required.");
100
+ }
73
101
  let referer = req.get("referer") || req.headers.referer;
74
102
  if (!referer) {
75
103
  throw new BadRequestError("Missing required fields: referer is required.");
76
104
  }
77
105
  referer = referer.replace(/\/$/, "");
78
- const user = await this.authService.getUserByEmail(email);
106
+ const organization = organizationId
107
+ ? await this.organizationService.findOne(EmptyUserContext, {
108
+ filters: { _id: { eq: organizationId } },
109
+ })
110
+ : null;
111
+ const userContext = {
112
+ ...EmptyUserContext,
113
+ organization: organization || undefined,
114
+ };
115
+ const user = await this.userService.findOne(userContext, {
116
+ filters: { email: { eq: email.toLowerCase() } },
117
+ });
79
118
  if (user) {
80
- await this.authService.sendResetPasswordEmail(email, referer);
119
+ await sendResetPasswordEmail(this.database, email, referer);
81
120
  }
82
121
  apiUtils.apiResponse(res, 200);
83
122
  }
84
123
  async resetPassword(req, res) {
85
- const { email, token, password } = req.body;
124
+ const { email, token, password, organizationId, } = req.body;
86
125
  if (!email || !token || !password) {
87
126
  throw new BadRequestError("Missing required fields: email, token, and password are required.");
88
127
  }
89
- const response = await this.authService.resetPassword(email, token, password);
128
+ if (config.app.isMultiTenant && !organizationId) {
129
+ throw new BadRequestError("Missing required fields: organizationId is required.");
130
+ }
131
+ const response = await resetPassword(this.database, email, token, password, organizationId);
90
132
  apiUtils.apiResponse(res, 200, { data: response });
91
133
  }
92
134
  }
@@ -1,7 +1,7 @@
1
- import { IAuthorization } from "../models/authorization.model.js";
1
+ import type { Application } from "express";
2
+ import type { IDatabase } from "../databases/models/index.js";
3
+ import { type IAuthorization } from "../models/authorization.model.js";
2
4
  import { ApiController } from "./api.controller.js";
3
- import { Application } from "express";
4
- import { IDatabase } from "../databases/models/index.js";
5
5
  export declare class AuthorizationsController extends ApiController<IAuthorization> {
6
6
  constructor(app: Application, database: IDatabase);
7
7
  }
@@ -1,9 +1,9 @@
1
- import { AuthorizationModelSpec } from "../models/authorization.model.js";
2
- import { ApiController } from "./api.controller.js";
1
+ import { AuthorizationModelSpec, } from "../models/authorization.model.js";
3
2
  import { MultiTenantApiService } from "../services/multi-tenant-api.service.js";
3
+ import { ApiController } from "./api.controller.js";
4
4
  export class AuthorizationsController extends ApiController {
5
5
  constructor(app, database) {
6
- const authorizationService = new MultiTenantApiService(database, 'authorizations', 'authorization', AuthorizationModelSpec);
7
- super('authorizations', app, authorizationService, 'authorization', AuthorizationModelSpec);
6
+ const authorizationService = new MultiTenantApiService(database, "authorizations", "authorization", AuthorizationModelSpec);
7
+ super("authorizations", app, authorizationService, "authorization", AuthorizationModelSpec);
8
8
  }
9
9
  }
@@ -1,7 +1,7 @@
1
+ import { Application } from "express";
1
2
  import { IDatabase } from "../databases/models/index.js";
2
3
  import { IRole } from "../models/role.model.js";
3
4
  import { ApiController } from "./api.controller.js";
4
- import { Application } from "express";
5
5
  export declare class RolesController extends ApiController<IRole> {
6
6
  constructor(app: Application, database: IDatabase);
7
7
  }
@@ -1,9 +1,9 @@
1
1
  import { RoleModelSpec } from "../models/role.model.js";
2
- import { ApiController } from "./api.controller.js";
3
2
  import { MultiTenantApiService } from "../services/multi-tenant-api.service.js";
3
+ import { ApiController } from "./api.controller.js";
4
4
  export class RolesController extends ApiController {
5
5
  constructor(app, database) {
6
- const roleService = new MultiTenantApiService(database, 'roles', 'role', RoleModelSpec);
7
- super('roles', app, roleService, 'role', RoleModelSpec, RoleModelSpec);
6
+ const roleService = new MultiTenantApiService(database, "roles", "role", RoleModelSpec);
7
+ super("roles", app, roleService, "role", RoleModelSpec, RoleModelSpec);
8
8
  }
9
9
  }
@@ -1,10 +1,9 @@
1
- import { Application } from 'express';
2
- import { IUser } from '@loomcore/common/models';
3
- import { ApiController } from './api.controller.js';
4
- import { UserService } from '../services/index.js';
5
- import { IDatabase } from '../databases/models/index.js';
1
+ import { type IUser } from "@loomcore/common/models";
2
+ import type { Application } from "express";
3
+ import type { IDatabase } from "../databases/models/index.js";
4
+ import { UserService } from "../services/index.js";
5
+ import { ApiController } from "./api.controller.js";
6
6
  export declare class UsersController extends ApiController<IUser> {
7
7
  userService: UserService;
8
8
  constructor(app: Application, database: IDatabase);
9
- mapRoutes(app: Application): void;
10
9
  }
@@ -1,22 +1,11 @@
1
- import { UserSpec, PublicUserSpec } from '@loomcore/common/models';
2
- import { ApiController } from './api.controller.js';
3
- import { isAuthorized } from '../middleware/index.js';
4
- import { UserService } from '../services/index.js';
1
+ import { PublicUserSpec, UserSpec } from "@loomcore/common/models";
2
+ import { UserService } from "../services/index.js";
3
+ import { ApiController } from "./api.controller.js";
5
4
  export class UsersController extends ApiController {
6
5
  userService;
7
6
  constructor(app, database) {
8
7
  const userService = new UserService(database);
9
- super('users', app, userService, 'user', UserSpec, PublicUserSpec);
8
+ super("users", app, userService, "user", UserSpec, PublicUserSpec);
10
9
  this.userService = userService;
11
10
  }
12
- mapRoutes(app) {
13
- app.get(`/api/${this.slug}`, isAuthorized(), this.get.bind(this));
14
- app.get(`/api/${this.slug}/all`, isAuthorized(), this.getAll.bind(this));
15
- app.get(`/api/${this.slug}/find`, isAuthorized(), this.get.bind(this));
16
- app.get(`/api/${this.slug}/count`, isAuthorized(), this.getCount.bind(this));
17
- app.get(`/api/${this.slug}/:id`, isAuthorized(), this.getById.bind(this));
18
- app.post(`/api/${this.slug}`, isAuthorized(), this.create.bind(this));
19
- app.patch(`/api/${this.slug}/:id`, isAuthorized(), this.partialUpdateById.bind(this));
20
- app.delete(`/api/${this.slug}/:id`, isAuthorized(), this.deleteById.bind(this));
21
- }
22
11
  }
@@ -1,6 +1,6 @@
1
- import type { PostgresConnection } from '../postgres-connection.js';
2
- import { IEntity } from "@loomcore/common/models";
1
+ import type { IEntity } from "@loomcore/common/models";
3
2
  import type { AppIdType } from "@loomcore/common/types";
3
+ import type { PostgresConnection } from "../postgres-connection.js";
4
4
  export declare function create<T extends IEntity>(client: PostgresConnection, pluralResourceName: string, entity: Partial<T>): Promise<{
5
5
  insertedId: AppIdType;
6
6
  entity: T;
@@ -1,12 +1,12 @@
1
1
  import { BadRequestError, DuplicateKeyError } from "../../../errors/index.js";
2
- import { columnsAndValuesFromEntity } from '../utils/columns-and-values-from-entity.js';
2
+ import { columnsAndValuesFromEntity } from "../utils/columns-and-values-from-entity.js";
3
3
  export async function create(client, pluralResourceName, entity) {
4
4
  try {
5
5
  delete entity._id;
6
6
  const { columns, values } = columnsAndValuesFromEntity(entity);
7
- const placeholders = columns.map((_, index) => `$${index + 1}`).join(', ');
7
+ const placeholders = columns.map((_, index) => `$${index + 1}`).join(", ");
8
8
  const query = `
9
- INSERT INTO "${pluralResourceName}" (${columns.join(', ')})
9
+ INSERT INTO "${pluralResourceName}" (${columns.join(", ")})
10
10
  VALUES (${placeholders})
11
11
  RETURNING *
12
12
  `;
@@ -18,11 +18,11 @@ export async function create(client, pluralResourceName, entity) {
18
18
  const insertedId = insertedRow._id;
19
19
  return {
20
20
  insertedId: insertedId,
21
- entity: insertedRow
21
+ entity: insertedRow,
22
22
  };
23
23
  }
24
24
  catch (err) {
25
- if (err.code === '23505') {
25
+ if (err.code === "23505") {
26
26
  throw new DuplicateKeyError(`${pluralResourceName} already exists`);
27
27
  }
28
28
  throw new BadRequestError(`Error creating ${pluralResourceName}: ${err.message}`);
@@ -1,9 +1,9 @@
1
- import { IQueryOptions, IModelSpec, IPagedResult, IEntity, IUserContextAuthorization } from "@loomcore/common/models";
1
+ import { IEntity, IModelSpec, IPagedResult, IQueryOptions, IUserContextAuthorization } from "@loomcore/common/models";
2
2
  import type { AppIdType } from "@loomcore/common/types";
3
3
  import { TSchema } from "@sinclair/typebox";
4
4
  import { DeleteResult, IDatabase } from "../models/index.js";
5
5
  import { Operation } from "../operations/operation.js";
6
- import type { PostgresConnection } from './postgres-connection.js';
6
+ import type { PostgresConnection } from "./postgres-connection.js";
7
7
  export declare class PostgresDatabase implements IDatabase {
8
8
  private connection;
9
9
  constructor(connection: PostgresConnection);
@@ -1,19 +1,19 @@
1
+ import { batchUpdate as batchUpdateCommand } from "./commands/postgres-batch-update.command.js";
1
2
  import { create as createCommand } from "./commands/postgres-create.command.js";
2
3
  import { createMany as createManyCommand } from "./commands/postgres-create-many.command.js";
3
- import { batchUpdate as batchUpdateCommand } from "./commands/postgres-batch-update.command.js";
4
+ import { deleteById as deleteByIdCommand } from "./commands/postgres-delete-by-id.command.js";
5
+ import { deleteMany as deleteManyCommand } from "./commands/postgres-delete-many.command.js";
4
6
  import { fullUpdateById as fullUpdateByIdCommand } from "./commands/postgres-full-update-by-id.command.js";
5
7
  import { partialUpdateById as partialUpdateByIdCommand } from "./commands/postgres-partial-update-by-id.command.js";
6
8
  import { update as updateCommand } from "./commands/postgres-update.command.js";
7
- import { deleteById as deleteByIdCommand } from "./commands/postgres-delete-by-id.command.js";
8
- import { deleteMany as deleteManyCommand } from "./commands/postgres-delete-many.command.js";
9
- import { findOne as findOneQuery } from "./queries/postgres-find-one.query.js";
10
9
  import { find as findQuery } from "./queries/postgres-find.query.js";
11
- import { getAll as getAllQuery } from "./queries/postgres-get-all.query.js";
10
+ import { findOne as findOneQuery } from "./queries/postgres-find-one.query.js";
12
11
  import { get as getQuery } from "./queries/postgres-get.query.js";
12
+ import { getAll as getAllQuery } from "./queries/postgres-get-all.query.js";
13
13
  import { getById as getByIdQuery } from "./queries/postgres-get-by-id.query.js";
14
14
  import { getCount as getCountQuery } from "./queries/postgres-get-count.query.js";
15
+ import { convertKeysToCamelCase, convertKeysToSnakeCase, } from "./utils/convert-keys.util.js";
15
16
  import { convertNullToUndefined } from "./utils/convert-null-to-undefined.util.js";
16
- import { convertKeysToSnakeCase, convertKeysToCamelCase } from "./utils/convert-keys.util.js";
17
17
  export class PostgresDatabase {
18
18
  connection;
19
19
  constructor(connection) {
@@ -72,7 +72,6 @@ export class PostgresDatabase {
72
72
  const now = new Date();
73
73
  let query = `
74
74
  SELECT DISTINCT
75
- ur."user_id" as "userId",
76
75
  r."name" as "role",
77
76
  f."name" as "feature",
78
77
  a."config",
@@ -96,7 +95,6 @@ export class PostgresDatabase {
96
95
  const result = await this.connection.query(query, values);
97
96
  const authorizations = [];
98
97
  for (const row of result.rows) {
99
- const userId = row.userId;
100
98
  authorizations.push({
101
99
  _id: row._id,
102
100
  _orgId: row._orgId,
@@ -1,3 +1,3 @@
1
- import { Request, Response, NextFunction } from 'express';
2
- declare const isAuthorized: (allowedFeatures?: string[]) => (req: Request, res: Response, next: NextFunction) => void;
1
+ import type { NextFunction, Request, Response } from "express";
2
+ declare const isAuthorized: (allowedFeatures?: string[]) => (req: Request, _res: Response, next: NextFunction) => void;
3
3
  export { isAuthorized };
@@ -1,13 +1,13 @@
1
- import { UserContextSpec } from '@loomcore/common/models';
2
- import { UnauthenticatedError, UnauthorizedError } from '../errors/index.js';
3
- import { JwtService } from '../services/index.js';
4
- import { config } from '../config/index.js';
1
+ import { UserContextSpec } from "@loomcore/common/models";
2
+ import jwt from "jsonwebtoken";
3
+ import { UnauthenticatedError, UnauthorizedError } from "../errors/index.js";
4
+ import { getAuthConfig } from "../utils/auth/get-auth-config.util.js";
5
5
  const isAuthorized = (allowedFeatures) => {
6
- return (req, res, next) => {
6
+ return (req, _res, next) => {
7
7
  let token = null;
8
8
  if (req.headers?.authorization) {
9
- let authHeader = req.headers.authorization;
10
- const authHeaderArray = authHeader.split('Bearer ');
9
+ const authHeader = req.headers.authorization;
10
+ const authHeaderArray = authHeader.split("Bearer ");
11
11
  if (authHeaderArray?.length > 1) {
12
12
  token = authHeaderArray[1];
13
13
  }
@@ -15,15 +15,16 @@ const isAuthorized = (allowedFeatures) => {
15
15
  if (!token) {
16
16
  throw new UnauthenticatedError();
17
17
  }
18
+ const authConfig = getAuthConfig();
18
19
  try {
19
- const rawPayload = JwtService.verify(token, config.auth?.clientSecret || '');
20
+ const rawPayload = jwt.verify(token, authConfig.clientSecret);
20
21
  const userContext = UserContextSpec.decode(rawPayload);
21
22
  req.userContext = userContext;
22
- if (userContext.authorizations.some(authorization => authorization.feature === 'admin')) {
23
+ if (userContext.authorizations.some((authorization) => authorization.feature === "admin")) {
23
24
  next();
24
25
  }
25
- else if (allowedFeatures && allowedFeatures.length) {
26
- if (!userContext.authorizations.some(authorization => allowedFeatures?.includes(authorization.feature))) {
26
+ else if (allowedFeatures?.length) {
27
+ if (!userContext.authorizations.some((authorization) => allowedFeatures.includes(authorization.feature))) {
27
28
  throw new UnauthorizedError();
28
29
  }
29
30
  next();
@@ -33,6 +34,7 @@ const isAuthorized = (allowedFeatures) => {
33
34
  }
34
35
  }
35
36
  catch (err) {
37
+ console.error(err);
36
38
  throw new UnauthenticatedError();
37
39
  }
38
40
  };
@@ -1,4 +1,4 @@
1
- import { IEntity } from "@loomcore/common/models";
1
+ import type { IEntity } from "@loomcore/common/models";
2
2
  import type { AppIdType } from "@loomcore/common/types";
3
3
  export interface IRefreshToken extends IEntity {
4
4
  token: string;
@@ -1,13 +1,12 @@
1
1
  import { entityUtils } from "@loomcore/common/utils";
2
- import { TypeboxIsoDate, getIdSchema } from "@loomcore/common/validation";
2
+ import { getIdSchema, TypeboxIsoDate } from "@loomcore/common/validation";
3
3
  import { Type } from "@sinclair/typebox";
4
- ;
5
4
  export const refreshTokenSchema = Type.Object({
6
5
  token: Type.String({ minLength: 1 }),
7
6
  deviceId: Type.String({ minLength: 1 }),
8
7
  userId: getIdSchema(),
9
8
  expiresOn: Type.Number(),
10
- created: TypeboxIsoDate({ title: 'Created Date' }),
11
- createdBy: getIdSchema()
9
+ created: TypeboxIsoDate({ title: "Created Date" }),
10
+ createdBy: getIdSchema(),
12
11
  });
13
12
  export const refreshTokenModelSpec = entityUtils.getModelSpec(refreshTokenSchema);