@drax/identity-back 0.11.4 → 0.12.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 (233) hide show
  1. package/dist/controllers/RoleController.js +8 -39
  2. package/dist/controllers/TenantController.js +1 -28
  3. package/dist/controllers/UserApiKeyController.js +6 -3
  4. package/dist/controllers/UserController.js +48 -209
  5. package/dist/errors/BadCredentialsError.js +12 -0
  6. package/dist/factory/RoleServiceFactory.js +1 -0
  7. package/dist/factory/TenantServiceFactory.js +1 -0
  8. package/dist/factory/UserApiKeyServiceFactory.js +5 -4
  9. package/dist/factory/UserServiceFactory.js +1 -0
  10. package/dist/graphql/resolvers/role.resolvers.js +2 -2
  11. package/dist/graphql/resolvers/tenant.resolvers.js +2 -2
  12. package/dist/graphql/resolvers/user-api-key.resolvers.js +2 -2
  13. package/dist/graphql/resolvers/user.resolvers.js +1 -1
  14. package/dist/graphql/types/userApiKey.graphql +1 -0
  15. package/dist/index.js +6 -0
  16. package/dist/middleware/apiKeyMiddleware.js +2 -2
  17. package/dist/models/RoleModel.js +10 -7
  18. package/dist/models/TenantModel.js +11 -8
  19. package/dist/models/UserApiKeyModel.js +15 -7
  20. package/dist/models/UserGroupModel.js +7 -7
  21. package/dist/models/UserModel.js +10 -8
  22. package/dist/permissions/UserApiKeyPermissions.js +2 -1
  23. package/dist/rbac/Rbac.js +10 -8
  24. package/dist/repository/mongo/RoleMongoRepository.js +20 -65
  25. package/dist/repository/mongo/TenantMongoRepository.js +18 -66
  26. package/dist/repository/mongo/UserApiKeyMongoRepository.js +29 -44
  27. package/dist/repository/mongo/UserMongoRepository.js +56 -85
  28. package/dist/repository/sqlite/RoleSqliteRepository.js +30 -115
  29. package/dist/repository/sqlite/TenantSqliteRepository.js +15 -105
  30. package/dist/repository/sqlite/UserApiKeySqliteRepository.js +42 -115
  31. package/dist/repository/sqlite/UserSqliteRepository.js +49 -130
  32. package/dist/routes/RoleRoutes.js +35 -10
  33. package/dist/routes/TenantRoutes.js +18 -9
  34. package/dist/routes/UserApiKeyRoutes.js +20 -4
  35. package/dist/routes/UserRoutes.js +92 -17
  36. package/dist/schemas/LoginSchema.js +9 -0
  37. package/dist/schemas/PasswordSchema.js +12 -0
  38. package/dist/schemas/RegisterSchema.js +19 -0
  39. package/dist/schemas/RoleSchema.js +23 -0
  40. package/dist/schemas/TenantSchema.js +13 -0
  41. package/dist/schemas/UserApiKeySchema.js +14 -0
  42. package/dist/schemas/UserSchema.js +39 -0
  43. package/dist/services/PermissionService.js +5 -5
  44. package/dist/services/RoleService.js +6 -6
  45. package/dist/services/TenantService.js +6 -6
  46. package/dist/services/UserApiKeyService.js +5 -5
  47. package/dist/services/UserService.js +14 -14
  48. package/dist/setup/CreateOrUpdateRole.js +5 -2
  49. package/dist/setup/CreateUserIfNotExist.js +3 -1
  50. package/dist/setup/RecoveryUserPassword.js +1 -1
  51. package/dist/zod/EndpointZod.js +9 -0
  52. package/dist/zod/TenantSchema.js +12 -0
  53. package/dist/zod/TenantZod.js +5 -3
  54. package/dist/zod/UserApiKeyZod.js +7 -3
  55. package/package.json +10 -9
  56. package/src/controllers/RoleController.ts +8 -36
  57. package/src/controllers/TenantController.ts +2 -25
  58. package/src/controllers/UserApiKeyController.ts +8 -3
  59. package/src/controllers/UserController.ts +50 -183
  60. package/src/errors/BadCredentialsError.ts +18 -1
  61. package/src/factory/RoleServiceFactory.ts +1 -0
  62. package/src/factory/TenantServiceFactory.ts +1 -0
  63. package/src/factory/UserApiKeyServiceFactory.ts +5 -4
  64. package/src/factory/UserServiceFactory.ts +1 -0
  65. package/src/graphql/resolvers/role.resolvers.ts +3 -2
  66. package/src/graphql/resolvers/tenant.resolvers.ts +3 -2
  67. package/src/graphql/resolvers/user-api-key.resolvers.ts +3 -2
  68. package/src/graphql/resolvers/user.resolvers.ts +2 -1
  69. package/src/graphql/types/userApiKey.graphql +1 -0
  70. package/src/index.ts +16 -0
  71. package/src/interfaces/ITenantRepository.ts +2 -2
  72. package/src/interfaces/IUserApiKeyRepository.ts +2 -2
  73. package/src/interfaces/IUserRepository.ts +3 -2
  74. package/src/middleware/apiKeyMiddleware.ts +2 -2
  75. package/src/models/RoleModel.ts +12 -7
  76. package/src/models/TenantModel.ts +13 -8
  77. package/src/models/UserApiKeyModel.ts +17 -7
  78. package/src/models/UserGroupModel.ts +7 -7
  79. package/src/models/UserModel.ts +10 -8
  80. package/src/permissions/UserApiKeyPermissions.ts +2 -1
  81. package/src/rbac/Rbac.ts +12 -9
  82. package/src/repository/mongo/RoleMongoRepository.ts +23 -94
  83. package/src/repository/mongo/TenantMongoRepository.ts +19 -98
  84. package/src/repository/mongo/UserApiKeyMongoRepository.ts +31 -53
  85. package/src/repository/mongo/UserMongoRepository.ts +71 -130
  86. package/src/repository/sqlite/RoleSqliteRepository.ts +37 -146
  87. package/src/repository/sqlite/TenantSqliteRepository.ts +16 -156
  88. package/src/repository/sqlite/UserApiKeySqliteRepository.ts +46 -149
  89. package/src/repository/sqlite/UserSqliteRepository.ts +59 -173
  90. package/src/routes/RoleRoutes.ts +35 -12
  91. package/src/routes/TenantRoutes.ts +25 -9
  92. package/src/routes/UserApiKeyRoutes.ts +23 -7
  93. package/src/routes/UserRoutes.ts +117 -34
  94. package/src/schemas/LoginSchema.ts +12 -0
  95. package/src/schemas/PasswordSchema.ts +16 -0
  96. package/src/{zod/UserZod.ts → schemas/RegisterSchema.ts} +7 -10
  97. package/src/schemas/RoleSchema.ts +29 -0
  98. package/src/schemas/TenantSchema.ts +22 -0
  99. package/src/{zod/UserApiKeyZod.ts → schemas/UserApiKeySchema.ts} +8 -3
  100. package/src/schemas/UserSchema.ts +57 -0
  101. package/src/services/PermissionService.ts +6 -5
  102. package/src/services/RoleService.ts +6 -6
  103. package/src/services/TenantService.ts +10 -10
  104. package/src/services/UserApiKeyService.ts +5 -5
  105. package/src/services/UserService.ts +15 -16
  106. package/src/setup/CreateOrUpdateRole.ts +7 -4
  107. package/src/setup/CreateUserIfNotExist.ts +5 -3
  108. package/src/setup/RecoveryUserPassword.ts +1 -1
  109. package/test/data-obj/apikey/root-mongo-user-apikey.ts +2 -1
  110. package/test/data-obj/roles/admin-sqlite-role.ts +2 -2
  111. package/test/data-obj/roles/operator-sqlite-role.ts +1 -1
  112. package/test/data-obj/tenants/company-sqlite-tenant.ts +6 -0
  113. package/test/data-obj/users/root-sqlite-user.ts +2 -2
  114. package/test/initializers/RoleSqliteInitializer.ts +1 -1
  115. package/test/repository/mongo/role-mongo-repository.test.ts +3 -3
  116. package/test/repository/mongo/user-apikey-mongo-repository.test.ts +5 -4
  117. package/test/repository/mongo/user-mongo-repository.test.ts +4 -4
  118. package/test/repository/sqlite/role-sqlite-repository.test.ts +21 -9
  119. package/test/repository/sqlite/tenant-sqlite-repository.test.ts +74 -0
  120. package/test/repository/sqlite/user-sqlite-repository.test.ts +15 -9
  121. package/test/routes/data/admin-role.ts +10 -0
  122. package/test/routes/data/root-user.ts +13 -0
  123. package/test/routes/helpers/CreateRootUserAndAdminRole.ts +17 -0
  124. package/test/routes/helpers/FastifyTestServerFactory.ts +34 -0
  125. package/test/routes/helpers/InitializePermissions.ts +23 -0
  126. package/test/routes/helpers/SetupIdentityDrax.ts +22 -0
  127. package/test/routes/tenant-route.test.ts +336 -0
  128. package/test/routes/user-route.test.ts +186 -0
  129. package/test/schemas/lab-schema.test.ts +110 -0
  130. package/test/service/mock-service.test.ts +3 -3
  131. package/test/service/role-service.test.ts +3 -3
  132. package/test/service/user-service.test.ts +16 -25
  133. package/test.db +0 -0
  134. package/tsconfig.tsbuildinfo +1 -1
  135. package/types/controllers/RoleController.d.ts +0 -1
  136. package/types/controllers/RoleController.d.ts.map +1 -1
  137. package/types/controllers/TenantController.d.ts +0 -1
  138. package/types/controllers/TenantController.d.ts.map +1 -1
  139. package/types/controllers/UserApiKeyController.d.ts.map +1 -1
  140. package/types/controllers/UserController.d.ts +11 -4
  141. package/types/controllers/UserController.d.ts.map +1 -1
  142. package/types/errors/BadCredentialsError.d.ts +9 -1
  143. package/types/errors/BadCredentialsError.d.ts.map +1 -1
  144. package/types/factory/RoleServiceFactory.d.ts.map +1 -1
  145. package/types/factory/TenantServiceFactory.d.ts.map +1 -1
  146. package/types/factory/UserApiKeyServiceFactory.d.ts.map +1 -1
  147. package/types/factory/UserServiceFactory.d.ts.map +1 -1
  148. package/types/graphql/resolvers/role.resolvers.d.ts +3 -9
  149. package/types/graphql/resolvers/role.resolvers.d.ts.map +1 -1
  150. package/types/graphql/resolvers/tenant.resolvers.d.ts +3 -9
  151. package/types/graphql/resolvers/tenant.resolvers.d.ts.map +1 -1
  152. package/types/graphql/resolvers/user-api-key.resolvers.d.ts +3 -9
  153. package/types/graphql/resolvers/user-api-key.resolvers.d.ts.map +1 -1
  154. package/types/graphql/resolvers/user.resolvers.d.ts +3 -9
  155. package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
  156. package/types/index.d.ts +5 -1
  157. package/types/index.d.ts.map +1 -1
  158. package/types/interfaces/ITenantRepository.d.ts +2 -2
  159. package/types/interfaces/ITenantRepository.d.ts.map +1 -1
  160. package/types/interfaces/IUserApiKeyRepository.d.ts +2 -2
  161. package/types/interfaces/IUserApiKeyRepository.d.ts.map +1 -1
  162. package/types/interfaces/IUserRepository.d.ts +3 -2
  163. package/types/interfaces/IUserRepository.d.ts.map +1 -1
  164. package/types/models/RoleModel.d.ts +7 -7
  165. package/types/models/RoleModel.d.ts.map +1 -1
  166. package/types/models/TenantModel.d.ts +7 -7
  167. package/types/models/TenantModel.d.ts.map +1 -1
  168. package/types/models/UserApiKeyModel.d.ts +7 -7
  169. package/types/models/UserApiKeyModel.d.ts.map +1 -1
  170. package/types/models/UserGroupModel.d.ts +2 -2
  171. package/types/models/UserGroupModel.d.ts.map +1 -1
  172. package/types/models/UserModel.d.ts +7 -7
  173. package/types/models/UserModel.d.ts.map +1 -1
  174. package/types/permissions/UserApiKeyPermissions.d.ts +2 -1
  175. package/types/permissions/UserApiKeyPermissions.d.ts.map +1 -1
  176. package/types/permissions/index.d.ts +1 -0
  177. package/types/permissions/index.d.ts.map +1 -1
  178. package/types/rbac/Rbac.d.ts +1 -1
  179. package/types/rbac/Rbac.d.ts.map +1 -1
  180. package/types/repository/mongo/RoleMongoRepository.d.ts +9 -11
  181. package/types/repository/mongo/RoleMongoRepository.d.ts.map +1 -1
  182. package/types/repository/mongo/TenantMongoRepository.d.ts +8 -11
  183. package/types/repository/mongo/TenantMongoRepository.d.ts.map +1 -1
  184. package/types/repository/mongo/UserApiKeyMongoRepository.d.ts +12 -5
  185. package/types/repository/mongo/UserApiKeyMongoRepository.d.ts.map +1 -1
  186. package/types/repository/mongo/UserMongoRepository.d.ts +11 -12
  187. package/types/repository/mongo/UserMongoRepository.d.ts.map +1 -1
  188. package/types/repository/sqlite/RoleSqliteRepository.d.ts +14 -14
  189. package/types/repository/sqlite/RoleSqliteRepository.d.ts.map +1 -1
  190. package/types/repository/sqlite/TenantSqliteRepository.d.ts +12 -14
  191. package/types/repository/sqlite/TenantSqliteRepository.d.ts.map +1 -1
  192. package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts +15 -11
  193. package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts.map +1 -1
  194. package/types/repository/sqlite/UserSqliteRepository.d.ts +15 -12
  195. package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -1
  196. package/types/routes/RoleRoutes.d.ts.map +1 -1
  197. package/types/routes/TenantRoutes.d.ts.map +1 -1
  198. package/types/routes/UserApiKeyRoutes.d.ts.map +1 -1
  199. package/types/routes/UserRoutes.d.ts.map +1 -1
  200. package/types/schemas/LoginSchema.d.ts +20 -0
  201. package/types/schemas/LoginSchema.d.ts.map +1 -0
  202. package/types/schemas/PasswordSchema.d.ts +27 -0
  203. package/types/schemas/PasswordSchema.d.ts.map +1 -0
  204. package/types/schemas/RegisterSchema.d.ts +32 -0
  205. package/types/schemas/RegisterSchema.d.ts.map +1 -0
  206. package/types/schemas/RoleSchema.d.ts +67 -0
  207. package/types/schemas/RoleSchema.d.ts.map +1 -0
  208. package/types/schemas/TenantSchema.d.ts +29 -0
  209. package/types/schemas/TenantSchema.d.ts.map +1 -0
  210. package/types/schemas/UserApiKeySchema.d.ts +39 -0
  211. package/types/schemas/UserApiKeySchema.d.ts.map +1 -0
  212. package/types/schemas/UserSchema.d.ts +161 -0
  213. package/types/schemas/UserSchema.d.ts.map +1 -0
  214. package/types/services/PermissionService.d.ts +1 -0
  215. package/types/services/PermissionService.d.ts.map +1 -1
  216. package/types/services/TenantService.d.ts +3 -3
  217. package/types/services/TenantService.d.ts.map +1 -1
  218. package/types/services/UserService.d.ts.map +1 -1
  219. package/types/setup/CreateOrUpdateRole.d.ts +2 -2
  220. package/types/setup/CreateOrUpdateRole.d.ts.map +1 -1
  221. package/types/setup/CreateUserIfNotExist.d.ts +2 -2
  222. package/types/setup/CreateUserIfNotExist.d.ts.map +1 -1
  223. package/types/zod/EndpointZod.d.ts +20 -0
  224. package/types/zod/EndpointZod.d.ts.map +1 -0
  225. package/types/zod/TenantSchema.d.ts +26 -0
  226. package/types/zod/TenantSchema.d.ts.map +1 -0
  227. package/types/zod/TenantZod.d.ts +13 -3
  228. package/types/zod/TenantZod.d.ts.map +1 -1
  229. package/types/zod/UserApiKeyZod.d.ts +23 -3
  230. package/types/zod/UserApiKeyZod.d.ts.map +1 -1
  231. package/types/zod/UserZod.d.ts +6 -6
  232. package/src/zod/RoleZod.ts +0 -14
  233. package/src/zod/TenantZod.ts +0 -14
@@ -1,130 +1,79 @@
1
- import sqlite from "better-sqlite3";
2
- import { randomUUID } from "node:crypto";
3
- import { SqliteErrorToValidationError, SqliteTableBuilder, SqlQueryFilter, SqlSort, ValidationError } from "@drax/common-back";
1
+ import { ValidationError } from "@drax/common-back";
4
2
  import RoleSqliteRepository from "./RoleSqliteRepository.js";
5
3
  import TenantSqliteRepository from "./TenantSqliteRepository.js";
6
- const tableFields = [
7
- { name: "name", type: "TEXT", unique: false, primary: false },
8
- { name: "username", type: "TEXT", unique: true, primary: false },
9
- { name: "active", type: "INTEGER", unique: false, primary: false },
10
- { name: "active", type: "INTEGER", unique: false, primary: false },
11
- { name: "password", type: "TEXT", unique: false, primary: false },
12
- { name: "email", type: "TEXT", unique: true, primary: false },
13
- { name: "phone", type: "TEXT", unique: false, primary: false },
14
- { name: "role", type: "TEXT", unique: false, primary: false },
15
- { name: "tenant", type: "TEXT", unique: false, primary: false },
16
- { name: "groups", type: "TEXT", unique: false, primary: false },
17
- { name: "avatar", type: "TEXT", unique: false, primary: false },
18
- { name: "origin", type: "TEXT", unique: false, primary: false },
19
- { name: "createdAt", type: "TEXT", unique: false, primary: false },
20
- { name: "updatedAt", type: "TEXT", unique: false, primary: false },
21
- { name: "emailVerified", type: "INTEGER", unique: false, primary: false },
22
- { name: "phoneVerified", type: "INTEGER", unique: false, primary: false },
23
- { name: "emailCode", type: "TEXT", unique: false, primary: false },
24
- { name: "phoneCode", type: "TEXT", unique: false, primary: false },
25
- ];
26
- class UserSqliteRepository {
4
+ import { AbstractSqliteRepository } from "@drax/crud-back";
5
+ class UserSqliteRepository extends AbstractSqliteRepository {
27
6
  constructor(dataBaseFile, verbose = false) {
28
- this.dataBaseFile = dataBaseFile;
29
- this.db = new sqlite(dataBaseFile, { verbose: verbose ? console.log : null });
7
+ super(dataBaseFile, verbose);
8
+ this.tableName = 'users';
9
+ this.searchFields = [];
10
+ this.booleanFields = ['active'];
11
+ this.identifier = '_id';
12
+ this.populateFields = [];
13
+ this.tableFields = [
14
+ { name: "name", type: "TEXT", unique: false, primary: false },
15
+ { name: "username", type: "TEXT", unique: true, primary: false },
16
+ { name: "active", type: "INTEGER", unique: false, primary: false },
17
+ { name: "password", type: "TEXT", unique: false, primary: false },
18
+ { name: "email", type: "TEXT", unique: true, primary: false },
19
+ { name: "phone", type: "TEXT", unique: false, primary: false },
20
+ { name: "role", type: "TEXT", unique: false, primary: false },
21
+ { name: "tenant", type: "TEXT", unique: false, primary: false },
22
+ { name: "groups", type: "TEXT", unique: false, primary: false },
23
+ { name: "avatar", type: "TEXT", unique: false, primary: false },
24
+ { name: "origin", type: "TEXT", unique: false, primary: false },
25
+ { name: "createdAt", type: "TEXT", unique: false, primary: false },
26
+ { name: "updatedAt", type: "TEXT", unique: false, primary: false },
27
+ { name: "emailVerified", type: "INTEGER", unique: false, primary: false },
28
+ { name: "phoneVerified", type: "INTEGER", unique: false, primary: false },
29
+ { name: "emailCode", type: "TEXT", unique: false, primary: false },
30
+ { name: "phoneCode", type: "TEXT", unique: false, primary: false },
31
+ ];
30
32
  this.roleRepository = new RoleSqliteRepository(dataBaseFile, verbose);
31
33
  this.tenantRepository = new TenantSqliteRepository(dataBaseFile, verbose);
32
- this.table();
33
34
  }
34
- table() {
35
- const builder = new SqliteTableBuilder(this.dataBaseFile, 'users', tableFields, false);
36
- builder.build('id');
37
- }
38
- normalizeData(userData) {
35
+ async prepareData(userData) {
39
36
  if (userData.groups && Array.isArray(userData.groups)) {
40
37
  userData.groups = userData.groups.join(",");
41
38
  }
42
39
  userData.active = userData.active ? 1 : 0;
43
- }
44
- async create(userData) {
45
- if (!userData.id) {
46
- userData.id = randomUUID();
47
- }
48
40
  if (!await this.findRoleById(userData.role)) {
49
41
  throw new ValidationError([{ field: 'role', reason: 'validation.notfound', value: userData.role }]);
50
42
  }
51
- userData.createdAt = (new Date().toISOString());
52
- this.normalizeData(userData);
53
- try {
54
- const fields = Object.keys(userData)
55
- .map(field => `${field}`)
56
- .join(', ');
57
- const values = Object.keys(userData)
58
- .map(field => `@${field}`)
59
- .join(', ');
60
- const stmt = this.db.prepare(`INSERT INTO users (${fields})
61
- VALUES (${values})`);
62
- stmt.run(userData);
63
- return this.findById(userData.id);
43
+ }
44
+ async prepareItem(user) {
45
+ if (user && user.role) {
46
+ user.role = await this.findRoleById(user.role);
64
47
  }
65
- catch (e) {
66
- throw SqliteErrorToValidationError(e, userData);
48
+ if (user && user.tenant) {
49
+ user.tenant = await this.findTenantById(user.tenant);
67
50
  }
68
51
  }
69
52
  async updatePartial(id, userData) {
70
53
  return this.update(id, userData);
71
54
  }
72
- async update(id, userData) {
73
- try {
74
- if (!await this.findRoleById(userData.role)) {
75
- throw new ValidationError([{ field: 'role', reason: 'validation.notfound', value: userData.role }]);
76
- }
77
- userData.updatedAt = (new Date().toISOString());
78
- this.normalizeData(userData);
79
- const setClauses = Object.keys(userData)
80
- .map(field => `${field} = @${field}`)
81
- .join(', ');
82
- userData.id = id;
83
- const stmt = this.db.prepare(`UPDATE users
84
- SET ${setClauses}
85
- WHERE id = @id `);
86
- stmt.run(userData);
87
- }
88
- catch (e) {
89
- throw SqliteErrorToValidationError(e, userData);
90
- }
91
- return this.findById(id);
92
- }
93
- async delete(id) {
94
- const stmt = this.db.prepare('DELETE FROM users WHERE id = ?');
95
- stmt.run(id);
96
- return true;
97
- }
98
- async deleteAll() {
99
- const stmt = this.db.prepare('DELETE FROM users');
100
- stmt.run();
101
- return true;
102
- }
103
- async findById(id) {
104
- const user = this.db.prepare('SELECT * FROM users WHERE id = ?').get(id);
55
+ async findByUsername(username) {
56
+ const user = this.db.prepare('SELECT * FROM users WHERE username = ?').get(username);
105
57
  if (!user) {
106
58
  return null;
107
59
  }
108
- user.role = await this.findRoleById(user.role);
109
- user.tenant = await this.findTenantById(user.tenant);
60
+ await this.decorate(user);
110
61
  return user;
111
62
  }
112
- async findByUsername(username) {
63
+ async findByUsernameWithPassword(username) {
113
64
  const user = this.db.prepare('SELECT * FROM users WHERE username = ?').get(username);
114
65
  if (!user) {
115
66
  return null;
116
67
  }
117
- user.role = await this.findRoleById(user.role);
118
- user.tenant = await this.findTenantById(user.tenant);
68
+ await this.decorate(user);
119
69
  return user;
120
70
  }
121
- async findByUsernameWithPassword(username) {
122
- const user = this.db.prepare('SELECT * FROM users WHERE username = ?').get(username);
71
+ async findByIdWithPassword(id) {
72
+ const user = this.db.prepare('SELECT * FROM users WHERE ${this.identifier} = ?').get(id);
123
73
  if (!user) {
124
74
  return null;
125
75
  }
126
- user.role = await this.findRoleById(user.role);
127
- user.tenant = await this.findTenantById(user.tenant);
76
+ await this.decorate(user);
128
77
  return user;
129
78
  }
130
79
  async findByEmail(email) {
@@ -132,8 +81,7 @@ class UserSqliteRepository {
132
81
  if (!user) {
133
82
  return null;
134
83
  }
135
- user.role = await this.findRoleById(user.role);
136
- user.tenant = await this.findTenantById(user.tenant);
84
+ await this.decorate(user);
137
85
  return user;
138
86
  }
139
87
  async findByEmailCode(code) {
@@ -141,8 +89,7 @@ class UserSqliteRepository {
141
89
  if (!user) {
142
90
  return null;
143
91
  }
144
- user.role = await this.findRoleById(user.role);
145
- user.tenant = await this.findTenantById(user.tenant);
92
+ await this.decorate(user);
146
93
  return user;
147
94
  }
148
95
  async findByRecoveryCode(code) {
@@ -150,8 +97,7 @@ class UserSqliteRepository {
150
97
  if (!user) {
151
98
  return null;
152
99
  }
153
- user.role = await this.findRoleById(user.role);
154
- user.tenant = await this.findTenantById(user.tenant);
100
+ await this.decorate(user);
155
101
  return user;
156
102
  }
157
103
  async findByPhoneCode(code) {
@@ -159,36 +105,9 @@ class UserSqliteRepository {
159
105
  if (!user) {
160
106
  return null;
161
107
  }
162
- user.role = await this.findRoleById(user.role);
163
- user.tenant = await this.findTenantById(user.tenant);
108
+ await this.decorate(user);
164
109
  return user;
165
110
  }
166
- async paginate({ page = 1, limit = 5, orderBy = '', order = false, search = '', filters = [] }) {
167
- const offset = page > 1 ? (page - 1) * limit : 0;
168
- let where = "";
169
- if (search) {
170
- where = ` WHERE (name LIKE '%${search}%' OR username LIKE '%${search}%') `;
171
- }
172
- where = SqlQueryFilter.applyFilters(where, filters);
173
- const sort = SqlSort.applySort(orderBy, order);
174
- console.log("paginate where ", where, "search", search, "filters", filters);
175
- const rCount = this.db.prepare('SELECT COUNT(*) as count FROM users' + where).get();
176
- where += sort;
177
- const users = this.db.prepare('SELECT * FROM users' + where + ' LIMIT ? OFFSET ?').all([limit, offset]);
178
- for (const user of users) {
179
- let role = await this.findRoleById(user.role);
180
- user.role = role ? role : null;
181
- let tenant = await this.findTenantById(user.tenant);
182
- user.tenant = tenant ? tenant : null;
183
- user.active = user.active === 1;
184
- }
185
- return {
186
- page: page,
187
- limit: limit,
188
- total: rCount.count,
189
- items: users
190
- };
191
- }
192
111
  async findRoleById(id) {
193
112
  return await this.roleRepository.findById(id);
194
113
  }
@@ -198,14 +117,14 @@ class UserSqliteRepository {
198
117
  async changePassword(id, password) {
199
118
  const stmt = this.db.prepare(`UPDATE users
200
119
  SET password = @password
201
- WHERE id = @id `);
120
+ WHERE ${this.identifier} = @id `);
202
121
  stmt.run({ id: id, password: password });
203
122
  return true;
204
123
  }
205
124
  async changeAvatar(id, avatar) {
206
125
  const stmt = this.db.prepare(`UPDATE users
207
126
  SET avatar = @avatar
208
- WHERE id = @id `);
127
+ WHERE ${this.identifier} = @id `);
209
128
  stmt.run({ id: id, avatar: avatar });
210
129
  return true;
211
130
  }
@@ -1,16 +1,41 @@
1
1
  import RoleController from "../controllers/RoleController.js";
2
+ import { CrudSchemaBuilder } from "@drax/crud-back";
3
+ import { RoleBaseSchema, RoleSchema } from "../schemas/RoleSchema.js";
4
+ import { zodToJsonSchema } from "zod-to-json-schema";
5
+ import zod from "zod";
2
6
  async function RoleRoutes(fastify, options) {
3
7
  const controller = new RoleController();
4
- fastify.get('/api/permissions', (req, rep) => controller.permissions(req, rep));
5
- fastify.get('/api/roles/export', (req, rep) => controller.export(req, rep));
6
- fastify.get('/api/roles/search', (req, rep) => controller.search(req, rep));
7
- fastify.get('/api/roles/:id', (req, rep) => controller.findById(req, rep));
8
- fastify.get('/api/roles/name/:name', (req, rep) => controller.findByName(req, rep));
9
- fastify.get('/api/roles/all', (req, rep) => controller.all(req, rep));
10
- fastify.get('/api/roles', (req, rep) => controller.paginate(req, rep));
11
- fastify.post('/api/roles', (req, rep) => controller.create(req, rep));
12
- fastify.put('/api/roles/:id', (req, rep) => controller.update(req, rep));
13
- fastify.delete('/api/roles/:id', (req, rep) => controller.delete(req, rep));
8
+ const schemas = new CrudSchemaBuilder(RoleSchema, RoleBaseSchema, RoleBaseSchema, 'role', 'openApi3', ['Identity']);
9
+ fastify.get('/api/roles/search', { schema: schemas.searchSchema }, (req, rep) => controller.search(req, rep));
10
+ fastify.get('/api/roles/:id', { schema: schemas.findByIdSchema }, (req, rep) => controller.findById(req, rep));
11
+ fastify.get('/api/roles/all', { schema: schemas.allSchema }, (req, rep) => controller.all(req, rep));
12
+ fastify.get('/api/roles', { schema: schemas.paginateSchema }, (req, rep) => controller.paginate(req, rep));
13
+ fastify.post('/api/roles', { schema: schemas.createSchema }, (req, rep) => controller.create(req, rep));
14
+ fastify.put('/api/roles/:id', { schema: schemas.updateSchema }, (req, rep) => controller.update(req, rep));
15
+ fastify.delete('/api/roles/:id', { schema: schemas.deleteSchema }, (req, rep) => controller.delete(req, rep));
16
+ fastify.get('/api/roles/export', { schema: schemas.exportSchema }, (req, rep) => controller.export(req, rep));
17
+ fastify.get('/api/permissions', {
18
+ schema: {
19
+ tags: ['Identity'],
20
+ response: {
21
+ 200: zodToJsonSchema(zod.array(zod.string())),
22
+ 401: schemas.jsonErrorBodyResponse,
23
+ 403: schemas.jsonErrorBodyResponse,
24
+ }
25
+ }
26
+ }, (req, rep) => controller.permissions(req, rep));
27
+ fastify.get('/api/roles/name/:name', {
28
+ schema: {
29
+ tags: ['Identity'],
30
+ params: zodToJsonSchema(zod.object({ name: zod.string() })),
31
+ response: {
32
+ 200: schemas.jsonEntitySchema,
33
+ 401: schemas.jsonErrorBodyResponse,
34
+ 403: schemas.jsonErrorBodyResponse,
35
+ 404: schemas.jsonErrorBodyResponse,
36
+ }
37
+ }
38
+ }, (req, rep) => controller.findByName(req, rep));
14
39
  }
15
40
  export default RoleRoutes;
16
41
  export { RoleRoutes };
@@ -1,15 +1,24 @@
1
+ import { CrudSchemaBuilder } from "@drax/crud-back";
1
2
  import TenantController from '../controllers/TenantController.js';
3
+ import { TenantSchema, TenantBaseSchema } from '../schemas/TenantSchema.js';
2
4
  async function TenantRoutes(fastify, options) {
3
5
  const controller = new TenantController();
4
- fastify.get('/api/tenants/export', (req, rep) => controller.export(req, rep));
5
- fastify.get('/api/tenants/search', (req, rep) => controller.search(req, rep));
6
- fastify.get('/api/tenants/:id', (req, rep) => controller.findById(req, rep));
7
- fastify.get('/api/tenants/name/:name', (req, rep) => controller.findByName(req, rep));
8
- fastify.get('/api/tenants/all', (req, rep) => controller.all(req, rep));
9
- fastify.get('/api/tenants', (req, rep) => controller.paginate(req, rep));
10
- fastify.post('/api/tenants', (req, rep) => controller.create(req, rep));
11
- fastify.put('/api/tenants/:id', (req, rep) => controller.update(req, rep));
12
- fastify.delete('/api/tenants/:id', (req, rep) => controller.delete(req, rep));
6
+ const schemas = new CrudSchemaBuilder(TenantSchema, TenantBaseSchema, TenantBaseSchema, 'tenant', 'openApi3', ['Identity']);
7
+ //Getters
8
+ fastify.get('/api/tenants/search', { schema: schemas.searchSchema }, (req, rep) => controller.search(req, rep));
9
+ fastify.get('/api/tenants/:id', { schema: schemas.findByIdSchema }, (req, rep) => controller.findById(req, rep));
10
+ fastify.get('/api/tenants/all', { schema: schemas.allSchema }, (req, rep) => controller.all(req, rep));
11
+ fastify.get('/api/tenants/find', { schema: schemas.findSchema }, (req, rep) => controller.find(req, rep));
12
+ fastify.get('/api/tenants/find-one', { schema: schemas.findOneSchema }, (req, rep) => controller.findOne(req, rep));
13
+ fastify.get('/api/tenants/find-by/:field/:value', { schema: schemas.findBySchema }, (req, rep) => controller.findBy(req, rep));
14
+ fastify.get('/api/tenants/find-one-by/:field/:value', { schema: schemas.findOneBySchema }, (req, rep) => controller.findOneBy(req, rep));
15
+ fastify.get('/api/tenants', { schema: schemas.paginateSchema }, (req, rep) => controller.paginate(req, rep));
16
+ //Mutations
17
+ fastify.post('/api/tenants', { schema: schemas.createSchema }, (req, rep) => controller.create(req, rep));
18
+ fastify.put('/api/tenants/:id', { schema: schemas.updateSchema }, (req, rep) => controller.update(req, rep));
19
+ fastify.delete('/api/tenants/:id', { schema: schemas.deleteSchema }, (req, rep) => controller.delete(req, rep));
20
+ //Others
21
+ fastify.get('/api/tenants/export', { schema: schemas.exportSchema }, (req, rep) => controller.export(req, rep));
13
22
  }
14
23
  export default TenantRoutes;
15
24
  export { TenantRoutes };
@@ -1,10 +1,26 @@
1
1
  import UserApiKeyController from "../controllers/UserApiKeyController.js";
2
2
  async function UserApiKeyRoutes(fastify, options) {
3
3
  const controller = new UserApiKeyController();
4
- fastify.get('/api/user-api-keys', (req, rep) => controller.paginate(req, rep));
5
- fastify.post('/api/user-api-keys', (req, rep) => controller.create(req, rep));
6
- fastify.put('/api/user-api-keys/:id', (req, rep) => controller.update(req, rep));
7
- fastify.delete('/api/user-api-keys/:id', (req, rep) => controller.delete(req, rep));
4
+ fastify.get('/api/user-api-keys', {
5
+ schema: {
6
+ tags: ['Identity'],
7
+ }
8
+ }, (req, rep) => controller.paginate(req, rep));
9
+ fastify.post('/api/user-api-keys', {
10
+ schema: {
11
+ tags: ['Identity'],
12
+ }
13
+ }, (req, rep) => controller.create(req, rep));
14
+ fastify.put('/api/user-api-keys/:id', {
15
+ schema: {
16
+ tags: ['Identity'],
17
+ }
18
+ }, (req, rep) => controller.update(req, rep));
19
+ fastify.delete('/api/user-api-keys/:id', {
20
+ schema: {
21
+ tags: ['Identity'],
22
+ }
23
+ }, (req, rep) => controller.delete(req, rep));
8
24
  }
9
25
  export default UserApiKeyRoutes;
10
26
  export { UserApiKeyRoutes };
@@ -1,23 +1,98 @@
1
1
  import UserController from "../controllers/UserController.js";
2
+ import { zodToJsonSchema } from "zod-to-json-schema";
3
+ import { CrudSchemaBuilder } from "@drax/crud-back";
4
+ import { LoginBodyRequestSchema, LoginBodyResponseSchema } from "../schemas/LoginSchema.js";
5
+ import { UserSchema, UserCreateSchema, UserUpdateSchema } from "../schemas/UserSchema.js";
6
+ import { RegisterBodyRequestSchema, RegisterBodyResponseSchema } from "../schemas/RegisterSchema.js";
7
+ import { MyPasswordBodyRequestSchema, PasswordBodyRequestSchema, PasswordBodyResponseSchema } from "../schemas/PasswordSchema.js";
2
8
  async function UserRoutes(fastify, options) {
3
9
  const controller = new UserController();
4
- fastify.post('/api/auth/login', (req, rep) => controller.auth(req, rep));
5
- fastify.get('/api/auth/me', (req, rep) => controller.me(req, rep));
6
- fastify.get('/api/users/search', (req, rep) => controller.search(req, rep));
7
- fastify.get('/api/users/export', (req, rep) => controller.export(req, rep));
8
- fastify.get('/api/users', (req, rep) => controller.paginate(req, rep));
9
- fastify.post('/api/users', (req, rep) => controller.create(req, rep));
10
- fastify.put('/api/users/:id', (req, rep) => controller.update(req, rep));
11
- fastify.delete('/api/users/:id', (req, rep) => controller.delete(req, rep));
12
- fastify.post('/api/users/register', (req, rep) => controller.register(req, rep));
13
- fastify.get('/api/users/verify-email/:code', (req, rep) => controller.verifyEmail(req, rep));
14
- fastify.get('/api/users/verify-phone/:code', (req, rep) => controller.verifyPhone(req, rep));
15
- fastify.post('/api/users/password/change', (req, rep) => controller.changeMyPassword(req, rep));
16
- fastify.post('/api/users/password/change/:id', (req, rep) => controller.changePassword(req, rep));
17
- fastify.post('/api/users/password/recovery/request', (req, rep) => controller.passwordRecoveryRequest(req, rep));
18
- fastify.post('/api/users/password/recovery/complete', (req, rep) => controller.recoveryPasswordComplete(req, rep));
19
- fastify.post('/api/users/avatar', (req, rep) => controller.updateAvatar(req, rep));
20
- fastify.get('/api/users/avatar/:filename', (req, rep) => controller.getAvatar(req, rep));
10
+ const schemas = new CrudSchemaBuilder(UserSchema, UserCreateSchema, UserUpdateSchema, 'tenant', 'openApi3', ['Identity']);
11
+ fastify.get('/api/users/search', { schema: schemas.searchSchema }, async (req, rep) => await controller.search(req, rep));
12
+ fastify.get('/api/users/export', { schema: schemas.exportSchema }, (req, rep) => controller.export(req, rep));
13
+ fastify.get('/api/users', { schema: schemas.paginateSchema }, (req, rep) => controller.paginate(req, rep));
14
+ fastify.post('/api/users', { schema: schemas.createSchema }, (req, rep) => controller.create(req, rep));
15
+ fastify.put('/api/users/:id', { schema: schemas.updateSchema }, (req, rep) => controller.update(req, rep));
16
+ fastify.delete('/api/users/:id', { schema: schemas.deleteSchema }, (req, rep) => controller.delete(req, rep));
17
+ fastify.post('/api/auth/login', {
18
+ schema: {
19
+ tags: ['Auth'],
20
+ body: zodToJsonSchema(LoginBodyRequestSchema),
21
+ response: {
22
+ 200: zodToJsonSchema(LoginBodyResponseSchema),
23
+ 400: schemas.jsonErrorBodyResponse,
24
+ },
25
+ },
26
+ }, (req, rep) => controller.auth(req, rep));
27
+ fastify.get('/api/auth/me', {
28
+ schema: {
29
+ tags: ['Auth'],
30
+ response: {
31
+ 200: schemas.jsonEntitySchema,
32
+ 401: schemas.jsonErrorBodyResponse,
33
+ 500: schemas.jsonErrorBodyResponse,
34
+ },
35
+ },
36
+ }, (req, rep) => controller.me(req, rep));
37
+ fastify.post('/api/users/register', {
38
+ schema: {
39
+ tags: ['Auth'],
40
+ body: zodToJsonSchema(RegisterBodyRequestSchema),
41
+ response: {
42
+ 200: zodToJsonSchema(RegisterBodyResponseSchema),
43
+ 400: schemas.jsonErrorBodyResponse,
44
+ 500: schemas.jsonErrorBodyResponse,
45
+ },
46
+ },
47
+ }, (req, rep) => controller.register(req, rep));
48
+ fastify.get('/api/users/verify-email/:code', {
49
+ schema: {
50
+ tags: ['Auth'],
51
+ }
52
+ }, (req, rep) => controller.verifyEmail(req, rep));
53
+ fastify.get('/api/users/verify-phone/:code', {
54
+ schema: {
55
+ tags: ['Auth'],
56
+ }
57
+ }, (req, rep) => controller.verifyPhone(req, rep));
58
+ fastify.post('/api/users/password/change', {
59
+ schema: {
60
+ tags: ['Auth'],
61
+ body: zodToJsonSchema(MyPasswordBodyRequestSchema),
62
+ 200: zodToJsonSchema(PasswordBodyResponseSchema),
63
+ 400: schemas.jsonErrorBodyResponse,
64
+ 500: schemas.jsonErrorBodyResponse,
65
+ }
66
+ }, (req, rep) => controller.changeMyPassword(req, rep));
67
+ fastify.post('/api/users/password/change/:id', {
68
+ schema: {
69
+ tags: ['Auth'],
70
+ body: zodToJsonSchema(PasswordBodyRequestSchema),
71
+ 200: zodToJsonSchema(PasswordBodyResponseSchema),
72
+ 400: schemas.jsonErrorBodyResponse,
73
+ 500: schemas.jsonErrorBodyResponse,
74
+ }
75
+ }, (req, rep) => controller.changePassword(req, rep));
76
+ fastify.post('/api/users/password/recovery/request', {
77
+ schema: {
78
+ tags: ['Auth'],
79
+ }
80
+ }, (req, rep) => controller.passwordRecoveryRequest(req, rep));
81
+ fastify.post('/api/users/password/recovery/complete', {
82
+ schema: {
83
+ tags: ['Auth'],
84
+ }
85
+ }, (req, rep) => controller.recoveryPasswordComplete(req, rep));
86
+ fastify.post('/api/users/avatar', {
87
+ schema: {
88
+ tags: ['Auth'],
89
+ }
90
+ }, (req, rep) => controller.updateAvatar(req, rep));
91
+ fastify.get('/api/users/avatar/:filename', {
92
+ schema: {
93
+ tags: ['Auth'],
94
+ }
95
+ }, (req, rep) => controller.getAvatar(req, rep));
21
96
  }
22
97
  export default UserRoutes;
23
98
  export { UserRoutes };
@@ -0,0 +1,9 @@
1
+ import z from "zod";
2
+ const LoginBodyRequestSchema = z.object({
3
+ username: z.string(),
4
+ password: z.string(),
5
+ });
6
+ const LoginBodyResponseSchema = z.object({
7
+ accessToken: z.string()
8
+ });
9
+ export { LoginBodyRequestSchema, LoginBodyResponseSchema };
@@ -0,0 +1,12 @@
1
+ import z from "zod";
2
+ const MyPasswordBodyRequestSchema = z.object({
3
+ currentPassword: z.string(),
4
+ newPassword: z.string(),
5
+ });
6
+ const PasswordBodyRequestSchema = z.object({
7
+ newPassword: z.string()
8
+ });
9
+ const PasswordBodyResponseSchema = z.object({
10
+ message: z.string()
11
+ });
12
+ export { MyPasswordBodyRequestSchema, PasswordBodyRequestSchema, PasswordBodyResponseSchema };
@@ -0,0 +1,19 @@
1
+ import z, { string } from "zod";
2
+ const RegisterBodyRequestSchema = z.object({
3
+ name: string({ required_error: "validation.required" })
4
+ .min(1, "validation.required"),
5
+ username: string({ required_error: "validation.required" })
6
+ .min(1, "validation.required"),
7
+ email: string({ required_error: "validation.required" })
8
+ .email("validation.email.invalid"),
9
+ phone: string({ required_error: "validation.required" }).optional(),
10
+ password: string({ required_error: "validation.required" })
11
+ .min(1, "validation.required")
12
+ .min(8, "validation.password.min8")
13
+ .max(64, "validation.password.max64"),
14
+ });
15
+ const RegisterBodyResponseSchema = z.object({
16
+ success: z.boolean(),
17
+ message: z.string().optional(),
18
+ });
19
+ export { RegisterBodyRequestSchema, RegisterBodyResponseSchema };
@@ -0,0 +1,23 @@
1
+ import { date, object, string, array, boolean } from "zod";
2
+ const RoleBaseSchema = object({
3
+ name: string({ required_error: "validation.required" })
4
+ .min(1, "validation.required")
5
+ .regex(/^[A-Z]/, "validation.startWithUpperCase"),
6
+ permissions: array(string()).optional(),
7
+ childRoles: array(string()).optional(),
8
+ });
9
+ const RoleSchema = RoleBaseSchema.extend({
10
+ _id: string(),
11
+ id: string().optional(),
12
+ permissions: array(string()).optional(),
13
+ readonly: boolean(),
14
+ childRoles: array(object({
15
+ _id: string(),
16
+ id: string().optional(),
17
+ name: string()
18
+ })).optional(),
19
+ createdAt: date().optional(),
20
+ updatedAt: date().optional()
21
+ });
22
+ export default RoleSchema;
23
+ export { RoleSchema, RoleBaseSchema };
@@ -0,0 +1,13 @@
1
+ import { object, string, date } from "zod";
2
+ const TenantBaseSchema = object({
3
+ name: string({ required_error: "validation.required" })
4
+ .min(1, "validation.required")
5
+ .regex(/^[A-Z]/, "validation.startWithUpperCase"),
6
+ });
7
+ const TenantSchema = TenantBaseSchema.extend({
8
+ _id: string(),
9
+ id: string().optional(),
10
+ createdAt: date(),
11
+ updatedAt: date()
12
+ });
13
+ export { TenantSchema, TenantBaseSchema };
@@ -0,0 +1,14 @@
1
+ import { array, object, string } from "zod";
2
+ const UserApiKeyBaseSchema = object({
3
+ name: string({ required_error: "validation.required" })
4
+ .min(1, "validation.required"),
5
+ ipv4: array(string().ip({ version: "v4", message: 'validation.invalidIpv4' })),
6
+ ipv6: array(string().ip({ version: "v6", message: 'validation.invalidIpv6' })),
7
+ });
8
+ const UserApiKeySchema = UserApiKeyBaseSchema.extend({
9
+ _id: string(),
10
+ id: string().optional(),
11
+ createdBy: string(),
12
+ });
13
+ export default UserApiKeyBaseSchema;
14
+ export { UserApiKeyBaseSchema, UserApiKeySchema };
@@ -0,0 +1,39 @@
1
+ import { date, object, string, boolean, array } from "zod";
2
+ const UserBaseSchema = object({
3
+ name: string({ required_error: "validation.required" })
4
+ .min(1, "validation.required"),
5
+ username: string({ required_error: "validation.required" })
6
+ .min(1, "validation.required"),
7
+ email: string({ required_error: "validation.required" })
8
+ .email("validation.email.invalid"),
9
+ phone: string({ required_error: "validation.required" }).optional(),
10
+ active: boolean().optional(),
11
+ role: string({ required_error: "validation.required" })
12
+ .min(1, "validation.required"),
13
+ tenant: string({ required_error: "validation.required" }).nullable().optional()
14
+ });
15
+ const UserCreateSchema = UserBaseSchema.extend({
16
+ password: string({ required_error: "validation.required" })
17
+ .min(1, "validation.required")
18
+ .min(8, "validation.password.min8")
19
+ .max(64, "validation.password.max64"),
20
+ });
21
+ const UserUpdateSchema = UserBaseSchema.extend({});
22
+ const UserSchema = UserBaseSchema
23
+ .extend({
24
+ _id: string(),
25
+ role: object({
26
+ _id: string(),
27
+ id: string().optional(),
28
+ name: string(),
29
+ permissions: array(string())
30
+ }).optional(),
31
+ active: boolean(),
32
+ tenant: object({
33
+ _id: string(),
34
+ id: string().optional(),
35
+ name: string(),
36
+ }).nullable().optional(),
37
+ createdAt: date().optional()
38
+ });
39
+ export { UserBaseSchema, UserSchema, UserCreateSchema, UserUpdateSchema, };