@drax/identity-back 0.0.8 → 0.0.10
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.
- package/dist/errors/BadCredentialsError.js +10 -0
- package/dist/errors/UnauthorizedError.js +10 -0
- package/dist/factory/RoleServiceFactory.js +16 -3
- package/dist/factory/UserServiceFactory.js +17 -3
- package/dist/graphql/resolvers/role.resolvers.js +98 -11
- package/dist/graphql/resolvers/user.resolvers.js +134 -15
- package/dist/graphql/types/role.graphql +6 -4
- package/dist/graphql/types/user.graphql +36 -9
- package/dist/i18n/messages/validation-i18n.js +21 -0
- package/dist/index.js +22 -7
- package/dist/interfaces/IID.js +1 -0
- package/dist/interfaces/IJwtUser.js +1 -0
- package/dist/middleware/jwtMiddleware.js +19 -0
- package/dist/middleware/rbacMiddleware.js +36 -0
- package/dist/models/RoleModel.js +0 -8
- package/dist/models/UserModel.js +1 -2
- package/dist/permissions/IdentityPermissions.js +16 -0
- package/dist/rbac/Rbac.js +20 -0
- package/dist/repository/mongo/RoleMongoRepository.js +41 -0
- package/dist/repository/mongo/UserMongoRepository.js +82 -0
- package/dist/repository/sqlite/RoleSqliteRepository.js +115 -0
- package/dist/repository/sqlite/UserSqliteRepository.js +157 -0
- package/dist/routes/RoleRoutes.js +145 -0
- package/dist/routes/UserRoutes.js +199 -0
- package/dist/routes/authRoutes.js +12 -4
- package/dist/services/AuthService.js +0 -15
- package/dist/services/PermissionService.js +19 -0
- package/dist/services/RoleService.js +48 -16
- package/dist/services/UserService.js +82 -23
- package/dist/utils/AuthUtils.js +20 -6
- package/dist/utils/DbSetupUtils.js +28 -0
- package/dist/zod/RoleZod.js +8 -0
- package/dist/zod/UserZod.js +18 -0
- package/package.json +17 -10
- package/src/errors/BadCredentialsError.ts +13 -0
- package/src/errors/UnauthorizedError.ts +13 -0
- package/src/factory/RoleServiceFactory.ts +20 -3
- package/src/factory/UserServiceFactory.ts +20 -3
- package/src/graphql/resolvers/role.resolvers.ts +92 -11
- package/src/graphql/resolvers/user.resolvers.ts +128 -15
- package/src/graphql/types/role.graphql +6 -4
- package/src/graphql/types/user.graphql +36 -9
- package/src/index.ts +50 -10
- package/src/interfaces/IID.ts +5 -0
- package/src/interfaces/IJwtUser.ts +7 -0
- package/src/interfaces/IRole.ts +15 -5
- package/src/interfaces/IRoleRepository.ts +8 -5
- package/src/interfaces/IUser.ts +30 -6
- package/src/interfaces/IUserGroup.ts +2 -1
- package/src/interfaces/IUserRepository.ts +11 -6
- package/src/middleware/jwtMiddleware.ts +22 -0
- package/src/middleware/rbacMiddleware.ts +40 -0
- package/src/models/RoleModel.ts +0 -9
- package/src/models/UserModel.ts +1 -2
- package/src/permissions/IdentityPermissions.ts +20 -0
- package/src/rbac/Rbac.ts +31 -0
- package/src/repository/mongo/RoleMongoRepository.ts +57 -0
- package/src/repository/mongo/UserMongoRepository.ts +104 -0
- package/src/repository/sqlite/RoleSqliteRepository.ts +151 -0
- package/src/repository/sqlite/UserSqliteRepository.ts +194 -0
- package/src/routes/RoleRoutes.ts +141 -0
- package/src/routes/UserRoutes.ts +198 -0
- package/src/services/PermissionService.ts +26 -0
- package/src/services/RoleService.ts +46 -21
- package/src/services/UserService.ts +86 -28
- package/src/utils/AuthUtils.ts +22 -7
- package/src/utils/DbSetupUtils.ts +39 -0
- package/src/zod/RoleZod.ts +14 -0
- package/src/zod/UserZod.ts +26 -0
- package/test/data-json/roles/admin-role.json +1 -1
- package/test/data-obj/roles/{admin-role.ts → admin-mongo-role.ts} +2 -1
- package/test/data-obj/roles/admin-sqlite-role.ts +9 -0
- package/test/data-obj/roles/operator-sqlite-role.ts +9 -0
- package/test/data-obj/users/root-mongo-user.ts +15 -0
- package/test/data-obj/users/root-sqlite-user.ts +16 -0
- package/test/{initializers → db}/MongoInMemory.ts +2 -1
- package/test/initializers/RoleMongoInitializer.ts +15 -0
- package/test/initializers/RoleSqliteInitializer.ts +18 -0
- package/test/repository/{role-repository.test.ts → mongo/role-mongo-repository.test.ts} +14 -24
- package/test/repository/mongo/user-mongo-repository.test.ts +121 -0
- package/test/repository/sqlite/role-sqlite-repository.test.ts +70 -0
- package/test/repository/sqlite/user-sqlite-repository.test.ts +126 -0
- package/test/service/mock-service.test.ts +3 -3
- package/test/service/role-service.test.ts +5 -5
- package/test/service/user-service.test.ts +42 -15
- package/test.db +0 -0
- package/tsconfig.json +16 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/types/errors/BadCredentialsError.d.ts +6 -0
- package/types/errors/BadCredentialsError.d.ts.map +1 -0
- package/types/errors/UnauthorizedError.d.ts +6 -0
- package/types/errors/UnauthorizedError.d.ts.map +1 -0
- package/types/factory/RoleServiceFactory.d.ts +2 -2
- package/types/factory/RoleServiceFactory.d.ts.map +1 -1
- package/types/factory/UserServiceFactory.d.ts +2 -2
- package/types/factory/UserServiceFactory.d.ts.map +1 -1
- package/types/graphql/resolvers/role.resolvers.d.ts +24 -7
- package/types/graphql/resolvers/role.resolvers.d.ts.map +1 -1
- package/types/graphql/resolvers/user.resolvers.d.ts +38 -7
- package/types/graphql/resolvers/user.resolvers.d.ts.map +1 -1
- package/types/i18n/messages/validation-i18n.d.ts +4 -0
- package/types/i18n/messages/validation-i18n.d.ts.map +1 -0
- package/types/index.d.ts +21 -5
- package/types/index.d.ts.map +1 -1
- package/types/interfaces/IID.d.ts +6 -0
- package/types/interfaces/IID.d.ts.map +1 -0
- package/types/interfaces/IJwtUser.d.ts +7 -0
- package/types/interfaces/IJwtUser.d.ts.map +1 -0
- package/types/interfaces/IRole.d.ts +13 -6
- package/types/interfaces/IRole.d.ts.map +1 -1
- package/types/interfaces/IRoleRepository.d.ts +8 -4
- package/types/interfaces/IRoleRepository.d.ts.map +1 -1
- package/types/interfaces/IUser.d.ts +29 -8
- package/types/interfaces/IUser.d.ts.map +1 -1
- package/types/interfaces/IUserGroup.d.ts +3 -2
- package/types/interfaces/IUserGroup.d.ts.map +1 -1
- package/types/interfaces/IUserRepository.d.ts +10 -6
- package/types/interfaces/IUserRepository.d.ts.map +1 -1
- package/types/middleware/jwtMiddleware.d.ts +4 -0
- package/types/middleware/jwtMiddleware.d.ts.map +1 -0
- package/types/middleware/rbacMiddleware.d.ts +4 -0
- package/types/middleware/rbacMiddleware.d.ts.map +1 -0
- package/types/models/RoleModel.d.ts +8 -8
- package/types/models/RoleModel.d.ts.map +1 -1
- package/types/models/UserGroupModel.d.ts +8 -8
- package/types/models/UserGroupModel.d.ts.map +1 -1
- package/types/models/UserModel.d.ts +8 -8
- package/types/models/UserModel.d.ts.map +1 -1
- package/types/permissions/IdentityPermissions.d.ts +16 -0
- package/types/permissions/IdentityPermissions.d.ts.map +1 -0
- package/types/rbac/Rbac.d.ts +12 -0
- package/types/rbac/Rbac.d.ts.map +1 -0
- package/types/repository/mongo/RoleMongoRepository.d.ts +14 -0
- package/types/repository/mongo/RoleMongoRepository.d.ts.map +1 -0
- package/types/repository/mongo/UserMongoRepository.d.ts +18 -0
- package/types/repository/mongo/UserMongoRepository.d.ts.map +1 -0
- package/types/repository/sqlite/RoleSqliteRepository.d.ts +19 -0
- package/types/repository/sqlite/RoleSqliteRepository.d.ts.map +1 -0
- package/types/repository/sqlite/UserSqliteRepository.d.ts +24 -0
- package/types/repository/sqlite/UserSqliteRepository.d.ts.map +1 -0
- package/types/routes/RoleRoutes.d.ts +4 -0
- package/types/routes/RoleRoutes.d.ts.map +1 -0
- package/types/routes/UserRoutes.d.ts +4 -0
- package/types/routes/UserRoutes.d.ts.map +1 -0
- package/types/routes/authRoutes.d.ts.map +1 -1
- package/types/services/AuthService.d.ts +0 -3
- package/types/services/AuthService.d.ts.map +1 -1
- package/types/services/PermissionService.d.ts +9 -0
- package/types/services/PermissionService.d.ts.map +1 -0
- package/types/services/RoleService.d.ts +6 -8
- package/types/services/RoleService.d.ts.map +1 -1
- package/types/services/UserService.d.ts +13 -11
- package/types/services/UserService.d.ts.map +1 -1
- package/types/utils/AuthUtils.d.ts +5 -2
- package/types/utils/AuthUtils.d.ts.map +1 -1
- package/types/utils/DbSetupUtils.d.ts +10 -0
- package/types/utils/DbSetupUtils.d.ts.map +1 -0
- package/types/zod/RoleZod.d.ts +10 -0
- package/types/zod/RoleZod.d.ts.map +1 -0
- package/types/zod/UserZod.d.ts +53 -0
- package/types/zod/UserZod.d.ts.map +1 -0
- package/dist/factory/AuthServiceFactory.js +0 -8
- package/dist/graphql/resolvers/auth.resolvers.js +0 -16
- package/dist/graphql/types/auth.graphql +0 -12
- package/dist/repository/RoleRepository.js +0 -29
- package/dist/repository/UserRepository.js +0 -33
- package/src/factory/AuthServiceFactory.ts +0 -10
- package/src/graphql/resolvers/auth.resolvers.ts +0 -20
- package/src/graphql/types/auth.graphql +0 -12
- package/src/repository/RoleRepository.ts +0 -42
- package/src/repository/UserRepository.ts +0 -47
- package/src/routes/authRoutes.ts +0 -22
- package/src/services/AuthService.ts +0 -29
- package/test/data-obj/users/root-user.ts +0 -15
- package/test/initializers/MongoInMemory.mjs +0 -34
- package/test/initializers/RoleInitializer.mjs +0 -11
- package/test/initializers/RoleInitializer.ts +0 -15
- package/test/repository/user-repository.test.ts +0 -54
- package/types/factory/AuthServiceFactory.d.ts +0 -4
- package/types/factory/AuthServiceFactory.d.ts.map +0 -1
- package/types/graphql/resolvers/auth.resolvers.d.ts +0 -12
- package/types/graphql/resolvers/auth.resolvers.d.ts.map +0 -1
- package/types/repository/RoleRepository.d.ts +0 -41
- package/types/repository/RoleRepository.d.ts.map +0 -1
- package/types/repository/UserRepository.d.ts +0 -40
- package/types/repository/UserRepository.d.ts.map +0 -1
- package/types/routes/AuthRoutes.d.ts +0 -3
- package/types/routes/AuthRoutes.d.ts.map +0 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {IUser} from './IUser'
|
|
1
|
+
import {IUser, IUserCreate, IUserUpdate} from './IUser'
|
|
2
|
+
import {IPaginateFilter, IPaginateResult} from "@drax/common-back";
|
|
3
|
+
import {IID} from "./IID";
|
|
2
4
|
interface IUserRepository{
|
|
3
|
-
create(role:
|
|
4
|
-
update(
|
|
5
|
-
delete(
|
|
6
|
-
findById(
|
|
5
|
+
create(role: IUserCreate): Promise<IUser>;
|
|
6
|
+
update(id: IID, updatedRole: IUserUpdate): Promise<IUser | null>;
|
|
7
|
+
delete(id: IID): Promise<boolean>;
|
|
8
|
+
findById(id: IID): Promise<IUser | null>;
|
|
7
9
|
findByUsername(username: string): Promise<IUser | null>;
|
|
8
|
-
paginate(
|
|
10
|
+
paginate(page?: number, limit?: number, search?: string, filters?: IPaginateFilter[]): Promise<IPaginateResult>;
|
|
11
|
+
changePassword(id: IID, password:string):Promise<Boolean>;
|
|
12
|
+
table?():void
|
|
13
|
+
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export {IUserRepository}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import AuthUtils from "../utils/AuthUtils.js";
|
|
2
|
+
import {IJwtUser} from "../interfaces/IJwtUser";
|
|
3
|
+
|
|
4
|
+
function jwtMiddleware (request, reply, done) {
|
|
5
|
+
try{
|
|
6
|
+
const token = request.headers?.authorization?.replace(/Bearer /i, "")
|
|
7
|
+
|
|
8
|
+
if(token){
|
|
9
|
+
const authUser = AuthUtils.verifyToken(token) as IJwtUser
|
|
10
|
+
if(authUser){
|
|
11
|
+
request.authUser = authUser
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
done()
|
|
15
|
+
}catch (e) {
|
|
16
|
+
console.error(e)
|
|
17
|
+
reply.code(401).send({ error: 'Token JWT inválido' });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default jwtMiddleware;
|
|
22
|
+
export {jwtMiddleware}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type {IJwtUser} from "../interfaces/IJwtUser";
|
|
2
|
+
import type {IRole, IRoleBase} from "../interfaces/IRole";
|
|
3
|
+
import {DraxCache} from "@drax/common-back";
|
|
4
|
+
import RoleServiceFactory from "../factory/RoleServiceFactory.js";
|
|
5
|
+
import Rbac from "../rbac/Rbac.js";
|
|
6
|
+
|
|
7
|
+
const cacheTTL = process.env.RBAC_CACHE_TTL ? parseInt(process.env.RBAC_CACHE_TTL) : 10000;
|
|
8
|
+
const draxCache = new DraxCache<IRoleBase>(cacheTTL);
|
|
9
|
+
const roleService = RoleServiceFactory
|
|
10
|
+
|
|
11
|
+
async function roleLoader(k):Promise<IRoleBase | null> {
|
|
12
|
+
const role: IRole = await roleService.findById(k)
|
|
13
|
+
if(role){
|
|
14
|
+
return {id: role.id, name: role.name, permissions: role.permissions} as IRoleBase
|
|
15
|
+
}
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function rbacMiddleware (request, reply) {
|
|
20
|
+
try{
|
|
21
|
+
//console.log("rbacMiddleware authUser",request.authUser)
|
|
22
|
+
if(request.authUser as IJwtUser){
|
|
23
|
+
const authUser = request.authUser
|
|
24
|
+
const cacheKey= authUser.roleId
|
|
25
|
+
const role = await draxCache.getOrLoad(cacheKey, roleLoader)
|
|
26
|
+
const rbac = new Rbac(authUser,role)
|
|
27
|
+
request.rbac = rbac
|
|
28
|
+
}else{
|
|
29
|
+
const rbac = new Rbac(null,null)
|
|
30
|
+
request.rbac = rbac
|
|
31
|
+
}
|
|
32
|
+
return
|
|
33
|
+
}catch (e) {
|
|
34
|
+
console.error(e)
|
|
35
|
+
reply.code(500).send({ error: 'RBAC ERROR' });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default rbacMiddleware;
|
|
40
|
+
export {rbacMiddleware}
|
package/src/models/RoleModel.ts
CHANGED
|
@@ -9,15 +9,6 @@ import {PaginateModel} from "mongoose";
|
|
|
9
9
|
const RoleSchema = new Schema<IRole>({
|
|
10
10
|
name: {
|
|
11
11
|
type: String, unique: true, required: true, index: true,
|
|
12
|
-
validate: [
|
|
13
|
-
{
|
|
14
|
-
validator: function(v) {
|
|
15
|
-
return !/(\s){2}/.test(v);
|
|
16
|
-
},
|
|
17
|
-
message: props => `Role name cant contain two spaces`
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
],
|
|
21
12
|
},
|
|
22
13
|
permissions: [{type: String, required: true}],
|
|
23
14
|
childRoles: [{
|
package/src/models/UserModel.ts
CHANGED
|
@@ -49,7 +49,6 @@ const UserSchema = new mongoose.Schema<IUser>({
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
avatar: {type: String, required: false},
|
|
52
|
-
avatarurl: {type: String, required: false},
|
|
53
52
|
role: {
|
|
54
53
|
type: mongoose.Schema.Types.ObjectId,
|
|
55
54
|
ref: 'Role',
|
|
@@ -69,7 +68,7 @@ const UserSchema = new mongoose.Schema<IUser>({
|
|
|
69
68
|
}
|
|
70
69
|
],
|
|
71
70
|
default: [],
|
|
72
|
-
|
|
71
|
+
id: false
|
|
73
72
|
}*/
|
|
74
73
|
}, {timestamps: true});
|
|
75
74
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
enum IdentityPermissions {
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
CreateUser = "user:create",
|
|
5
|
+
UpdateUser = "user:update",
|
|
6
|
+
DeleteUser = "user:delete",
|
|
7
|
+
ViewUser = "user:view",
|
|
8
|
+
ManageUser = "user:manage",
|
|
9
|
+
|
|
10
|
+
CreateRole = "role:create",
|
|
11
|
+
UpdateRole = "role:update",
|
|
12
|
+
DeleteRole = "role:delete",
|
|
13
|
+
ViewRole = "role:view",
|
|
14
|
+
ManageRole = "role:manage",
|
|
15
|
+
PermissionsRole = "role:permissions",
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default IdentityPermissions;
|
|
20
|
+
export {IdentityPermissions};
|
package/src/rbac/Rbac.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {IRoleBase} from "../interfaces/IRole";
|
|
2
|
+
import {IJwtUser} from "identity";
|
|
3
|
+
import UnauthorizedError from "../errors/UnauthorizedError.js";
|
|
4
|
+
|
|
5
|
+
class Rbac{
|
|
6
|
+
private role: IRoleBase;
|
|
7
|
+
private authUser: IJwtUser;
|
|
8
|
+
|
|
9
|
+
constructor(authUser: IJwtUser, role: IRoleBase) {
|
|
10
|
+
this.authUser = authUser;
|
|
11
|
+
this.role = role;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
hasPermission(requiredPermission: string): boolean {
|
|
15
|
+
if (!this.authUser || !this.role || !this.role.permissions || this.role.permissions.length === 0) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return this.role.permissions.includes(requiredPermission);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
assertPermission(requiredPermission: string) {
|
|
23
|
+
if(!this.hasPermission(requiredPermission)){
|
|
24
|
+
throw new UnauthorizedError()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default Rbac;
|
|
31
|
+
export {Rbac}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {RoleModel} from "../../models/RoleModel.js";
|
|
2
|
+
import {IRole} from '../../interfaces/IRole'
|
|
3
|
+
import {IRoleRepository} from '../../interfaces/IRoleRepository'
|
|
4
|
+
import {IPaginateFilter, IPaginateResult, mongoose} from "@drax/common-back";
|
|
5
|
+
import {FilterQuery, PaginateOptions, PaginateResult} from "mongoose";
|
|
6
|
+
import {DeleteResult} from "mongodb";
|
|
7
|
+
|
|
8
|
+
class RoleMongoRepository implements IRoleRepository{
|
|
9
|
+
|
|
10
|
+
async create(roleData: IRole): Promise<IRole> {
|
|
11
|
+
const role : mongoose.HydratedDocument<IRole> = new RoleModel(roleData)
|
|
12
|
+
await role.save()
|
|
13
|
+
return role
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async update(id: mongoose.Types.ObjectId | string, roleData: IRole): Promise<IRole> {
|
|
17
|
+
const role : mongoose.HydratedDocument<IRole> = await RoleModel.findOneAndUpdate({_id: id}, roleData, {new: true}).exec()
|
|
18
|
+
return role
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async delete(id: mongoose.Types.ObjectId): Promise<boolean> {
|
|
22
|
+
const result : DeleteResult = await RoleModel.deleteOne(id).exec()
|
|
23
|
+
return result.deletedCount == 1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async findById(id: mongoose.Types.ObjectId): Promise<IRole | null>{
|
|
27
|
+
const role: mongoose.HydratedDocument<IRole> | null = await RoleModel.findById(id).exec()
|
|
28
|
+
return role
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async fetchAll(): Promise<IRole[]>{
|
|
32
|
+
const roles: mongoose.HydratedDocument<IRole>[] = await RoleModel.find().exec()
|
|
33
|
+
return roles
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async paginate(page:number = 1, limit:number = 5, search:string): Promise<IPaginateResult>{
|
|
37
|
+
|
|
38
|
+
const query = {}
|
|
39
|
+
|
|
40
|
+
if(search){
|
|
41
|
+
query['$or'] = [
|
|
42
|
+
{name: new RegExp(search, 'i')},
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const options = {page, limit} as PaginateOptions
|
|
47
|
+
const roles: PaginateResult<IRole> = await RoleModel.paginate(query, options)
|
|
48
|
+
return {
|
|
49
|
+
page: page,
|
|
50
|
+
limit: limit,
|
|
51
|
+
total: roles.totalDocs,
|
|
52
|
+
items: roles.docs
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default RoleMongoRepository
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {UserModel} from "../../models/UserModel.js";
|
|
2
|
+
import {mongoose, MongooseErrorToValidationError, MongoServerErrorToValidationError, ValidationError} from "@drax/common-back"
|
|
3
|
+
import type {IPaginateFilter, IPaginateResult} from "@drax/common-back"
|
|
4
|
+
import type {IUser, IUserCreate, IUserUpdate} from "../../interfaces/IUser";
|
|
5
|
+
import {DeleteResult, MongoServerError} from "mongodb";
|
|
6
|
+
import type {IUserRepository} from "../../interfaces/IUserRepository";
|
|
7
|
+
import {PaginateResult} from "mongoose";
|
|
8
|
+
import RoleMongoRepository from "./RoleMongoRepository.js";
|
|
9
|
+
|
|
10
|
+
class UserMongoRepository implements IUserRepository {
|
|
11
|
+
private roleRepository: RoleMongoRepository;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this.roleRepository = new RoleMongoRepository()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async create(userData: IUserCreate): Promise<IUser> {
|
|
19
|
+
try{
|
|
20
|
+
|
|
21
|
+
if(!await this.roleRepository.findById(userData.role as mongoose.Types.ObjectId)){
|
|
22
|
+
throw new ValidationError([{field: 'role', reason: 'validation.notfound', value: userData.role}])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const user: mongoose.HydratedDocument<IUser> = new UserModel(userData)
|
|
26
|
+
await user.save()
|
|
27
|
+
await user.populate('role')
|
|
28
|
+
return user
|
|
29
|
+
}catch (e){
|
|
30
|
+
if(e instanceof mongoose.Error.ValidationError){
|
|
31
|
+
throw MongooseErrorToValidationError(e)
|
|
32
|
+
}
|
|
33
|
+
throw e
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async update(id: mongoose.Types.ObjectId, userData: IUserUpdate): Promise<IUser> {
|
|
39
|
+
try{
|
|
40
|
+
const user: mongoose.HydratedDocument<IUser> = await UserModel.findOneAndUpdate({_id: id}, userData, {new: true}).populate('role').exec()
|
|
41
|
+
return user
|
|
42
|
+
}catch (e){
|
|
43
|
+
if(e instanceof mongoose.Error.ValidationError){
|
|
44
|
+
throw MongooseErrorToValidationError(e)
|
|
45
|
+
}
|
|
46
|
+
if(e instanceof MongoServerError || e.name === 'MongoServerError'){
|
|
47
|
+
throw MongoServerErrorToValidationError(e)
|
|
48
|
+
}
|
|
49
|
+
throw e
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async delete(id: mongoose.Types.ObjectId): Promise<boolean> {
|
|
54
|
+
const result: DeleteResult = await UserModel.deleteOne({_id: id}).exec()
|
|
55
|
+
return result.deletedCount == 1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async findById(id: mongoose.Types.ObjectId): Promise<IUser> {
|
|
60
|
+
const user: mongoose.HydratedDocument<IUser> = await UserModel.findById(id).populate('role').exec()
|
|
61
|
+
return user
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async findByUsername(username: string): Promise<IUser> {
|
|
65
|
+
const user: mongoose.HydratedDocument<IUser> = await UserModel.findOne({username: username}).populate('role').exec()
|
|
66
|
+
return user
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async paginate(page: number = 1, limit: number = 5, search?:string): Promise<IPaginateResult> {
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
const query = {}
|
|
73
|
+
|
|
74
|
+
if(search){
|
|
75
|
+
query['$or'] = [
|
|
76
|
+
{username: new RegExp(search, 'i')},
|
|
77
|
+
{email: new RegExp(search, 'i')},
|
|
78
|
+
{name: new RegExp(search, 'i')},
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const options = {populate: ['role'], page: page, limit: limit }
|
|
83
|
+
|
|
84
|
+
const userPaginated: PaginateResult<IUser> = await UserModel.paginate(query, options)
|
|
85
|
+
return {
|
|
86
|
+
page: page,
|
|
87
|
+
limit: limit,
|
|
88
|
+
total: userPaginated.totalDocs,
|
|
89
|
+
items: userPaginated.docs
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async changePassword(id: mongoose.Types.ObjectId, password: string):Promise<boolean> {
|
|
94
|
+
try{
|
|
95
|
+
await UserModel.findOneAndUpdate({_id: id}, {password}).exec()
|
|
96
|
+
return true
|
|
97
|
+
}catch (e){
|
|
98
|
+
console.error(e)
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default UserMongoRepository
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {IRole} from '../../interfaces/IRole'
|
|
2
|
+
import {IRoleRepository} from '../../interfaces/IRoleRepository'
|
|
3
|
+
import {UUID} from "crypto";
|
|
4
|
+
import sqlite from "better-sqlite3";
|
|
5
|
+
import {randomUUID} from "node:crypto";
|
|
6
|
+
import {IPaginateFilter, IPaginateResult, ValidationError} from "@drax/common-back";
|
|
7
|
+
import {SqliteErrorToValidationError} from "@drax/common-back";
|
|
8
|
+
import {IID} from "../../interfaces/IID";
|
|
9
|
+
|
|
10
|
+
const roleTableSQL: string = `
|
|
11
|
+
CREATE TABLE IF NOT EXISTS roles
|
|
12
|
+
(
|
|
13
|
+
id TEXT PRIMARY KEY,
|
|
14
|
+
name TEXT,
|
|
15
|
+
permissions TEXT,
|
|
16
|
+
readonly INTEGER,
|
|
17
|
+
childRoles TEXT
|
|
18
|
+
|
|
19
|
+
);
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
class RoleSqliteRepository implements IRoleRepository{
|
|
23
|
+
|
|
24
|
+
private db: any;
|
|
25
|
+
|
|
26
|
+
constructor(DATABASE:string, verbose:boolean = false) {
|
|
27
|
+
this.db = new sqlite(DATABASE, {verbose: verbose ? console.log : null});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
table() {
|
|
31
|
+
this.db.exec(roleTableSQL);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
normalizeData(roleData: IRole){
|
|
35
|
+
|
|
36
|
+
roleData.readonly = roleData.readonly ? 1 : 0
|
|
37
|
+
|
|
38
|
+
if(roleData.permissions && Array.isArray(roleData.permissions)){
|
|
39
|
+
roleData.permissions = roleData.permissions.join(",")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if(roleData.childRoles && Array.isArray(roleData.childRoles)){
|
|
43
|
+
roleData.childRoles = roleData.childRoles.join(",")
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async create(roleData: IRole): Promise<IRole> {
|
|
48
|
+
try{
|
|
49
|
+
|
|
50
|
+
if(!roleData.id){
|
|
51
|
+
roleData.id = randomUUID()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.normalizeData(roleData)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
const fields = Object.keys(roleData)
|
|
58
|
+
.map(field => `${field}`)
|
|
59
|
+
.join(', ');
|
|
60
|
+
|
|
61
|
+
const values = Object.keys(roleData)
|
|
62
|
+
.map(field => `@${field}`)
|
|
63
|
+
.join(', ');
|
|
64
|
+
|
|
65
|
+
/* console.log("fields", fields)
|
|
66
|
+
console.log("values",values)
|
|
67
|
+
console.log("userData",roleData)*/
|
|
68
|
+
|
|
69
|
+
const stmt = this.db.prepare(`INSERT INTO roles (${fields}) VALUES (${values})`);
|
|
70
|
+
stmt.run(roleData)
|
|
71
|
+
return this.findById(roleData.id as UUID)
|
|
72
|
+
}catch (e){
|
|
73
|
+
console.log(e)
|
|
74
|
+
throw SqliteErrorToValidationError(e, roleData)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async findById(id: IID): Promise<IRole | null>{
|
|
79
|
+
const role = this.db.prepare('SELECT * FROM roles WHERE id = ?').get(id);
|
|
80
|
+
if(role){
|
|
81
|
+
role.permissions = role.permissions ? role.permissions.split(",") : []
|
|
82
|
+
return role
|
|
83
|
+
}
|
|
84
|
+
return undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async update(id: IID, roleData: IRole): Promise<IRole> {
|
|
88
|
+
try{
|
|
89
|
+
this.normalizeData(roleData)
|
|
90
|
+
const setClauses = Object.keys(roleData)
|
|
91
|
+
.map(field => `${field} = @${field}`)
|
|
92
|
+
.join(', ');
|
|
93
|
+
roleData.id = id
|
|
94
|
+
const stmt = this.db.prepare( `UPDATE roles SET ${setClauses} WHERE id = @id `);
|
|
95
|
+
stmt.run(roleData);
|
|
96
|
+
return this.findById(id)
|
|
97
|
+
}catch (e){
|
|
98
|
+
console.log(e)
|
|
99
|
+
throw SqliteErrorToValidationError(e, roleData)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async delete(id: IID): Promise<boolean> {
|
|
105
|
+
const stmt = this.db.prepare('DELETE FROM roles WHERE id = ?');
|
|
106
|
+
stmt.run(id);
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async deleteAll(): Promise<boolean> {
|
|
111
|
+
const stmt = this.db.prepare('DELETE FROM roles');
|
|
112
|
+
stmt.run();
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async fetchAll(): Promise<IRole[]>{
|
|
117
|
+
const roles = this.db.prepare('SELECT * FROM roles').all();
|
|
118
|
+
for (const role of roles) {
|
|
119
|
+
role.permissions = role.permissions? role.permissions.split(",") : []
|
|
120
|
+
}
|
|
121
|
+
return roles
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async paginate(page = 1, limit = 5, search=""): Promise<IPaginateResult>{
|
|
125
|
+
const offset = page > 1 ? (page - 1) * limit : 0
|
|
126
|
+
|
|
127
|
+
let where
|
|
128
|
+
if (search) {
|
|
129
|
+
where = ` WHERE name LIKE '%${search}%'`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM roles'+where).get();
|
|
133
|
+
const roles = this.db.prepare('SELECT * FROM roles LIMIT ? OFFSET ?'+where).all([limit, offset]);
|
|
134
|
+
|
|
135
|
+
for (const role of roles) {
|
|
136
|
+
role.permissions = role.permissions? role.permissions.split(",") : []
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
page: page,
|
|
142
|
+
limit: limit,
|
|
143
|
+
total: rCount.count,
|
|
144
|
+
items: roles
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default RoleSqliteRepository
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {IUser, IUserCreate, IUserUpdate} from "../../interfaces/IUser";
|
|
2
|
+
import sqlite from "better-sqlite3";
|
|
3
|
+
import {randomUUID} from "node:crypto";
|
|
4
|
+
import {UUID} from "crypto";
|
|
5
|
+
import {IUserRepository} from "../../interfaces/IUserRepository";
|
|
6
|
+
import type {IPaginateResult} from "@drax/common-back";
|
|
7
|
+
import {mongoose, SqliteErrorToValidationError, ValidationError} from "@drax/common-back";
|
|
8
|
+
import RoleSqliteRepository from "./RoleSqliteRepository.js";
|
|
9
|
+
import {IID} from "../../interfaces/IID";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const userTableSQL: string = `
|
|
13
|
+
CREATE TABLE IF NOT EXISTS users
|
|
14
|
+
(
|
|
15
|
+
id
|
|
16
|
+
TEXT
|
|
17
|
+
PRIMARY
|
|
18
|
+
KEY,
|
|
19
|
+
name
|
|
20
|
+
TEXT,
|
|
21
|
+
username
|
|
22
|
+
TEXT
|
|
23
|
+
UNIQUE,
|
|
24
|
+
active
|
|
25
|
+
INTEGER,
|
|
26
|
+
password
|
|
27
|
+
TEXT,
|
|
28
|
+
email
|
|
29
|
+
TEXT
|
|
30
|
+
UNIQUE,
|
|
31
|
+
phone
|
|
32
|
+
TEXT,
|
|
33
|
+
role
|
|
34
|
+
TEXT,
|
|
35
|
+
groups
|
|
36
|
+
TEXT,
|
|
37
|
+
avatar
|
|
38
|
+
TEXT
|
|
39
|
+
);
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
class UserSqliteRepository implements IUserRepository {
|
|
43
|
+
private db: any;
|
|
44
|
+
private roleRepository: RoleSqliteRepository;
|
|
45
|
+
|
|
46
|
+
constructor(DATABASE: string, verbose: boolean = false) {
|
|
47
|
+
this.db = new sqlite(DATABASE, {verbose: verbose ? console.log : null});
|
|
48
|
+
this.roleRepository = new RoleSqliteRepository(DATABASE, verbose)
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
table() {
|
|
53
|
+
this.db.exec(userTableSQL);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
normalizeData(userData: IUserCreate | IUserUpdate): void {
|
|
57
|
+
if (userData.groups && Array.isArray(userData.groups)) {
|
|
58
|
+
userData.groups = userData.groups.join(",")
|
|
59
|
+
}
|
|
60
|
+
userData.active = userData.active ? 1 : 0
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async create(userData: IUserCreate): Promise<IUser> {
|
|
64
|
+
if (!userData.id) {
|
|
65
|
+
userData.id = randomUUID()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!await this.findRoleById(userData.role)) {
|
|
69
|
+
throw new ValidationError([{field: 'role', reason: 'validation.notfound', value: userData.role}])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.normalizeData(userData)
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
|
|
76
|
+
const fields = Object.keys(userData)
|
|
77
|
+
.map(field => `${field}`)
|
|
78
|
+
.join(', ');
|
|
79
|
+
|
|
80
|
+
const values = Object.keys(userData)
|
|
81
|
+
.map(field => `@${field}`)
|
|
82
|
+
.join(', ');
|
|
83
|
+
|
|
84
|
+
/*console.log("fields", fields)
|
|
85
|
+
console.log("values",values)
|
|
86
|
+
console.log("userData",userData)*/
|
|
87
|
+
|
|
88
|
+
const stmt = this.db.prepare(`INSERT INTO users (${fields})
|
|
89
|
+
VALUES (${values})`);
|
|
90
|
+
stmt.run(userData)
|
|
91
|
+
return this.findById(userData.id as UUID)
|
|
92
|
+
} catch (e) {
|
|
93
|
+
throw SqliteErrorToValidationError(e, userData)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async update(id: UUID, userData: IUserUpdate): Promise<IUser> {
|
|
99
|
+
try {
|
|
100
|
+
if (!await this.findRoleById(userData.role)) {
|
|
101
|
+
throw new ValidationError([{field: 'role', reason: 'validation.notfound', value: userData.role}])
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.normalizeData(userData)
|
|
105
|
+
|
|
106
|
+
const setClauses = Object.keys(userData)
|
|
107
|
+
.map(field => `${field} = @${field}`)
|
|
108
|
+
.join(', ');
|
|
109
|
+
userData.id = id
|
|
110
|
+
const stmt = this.db.prepare(`UPDATE users
|
|
111
|
+
SET ${setClauses}
|
|
112
|
+
WHERE id = @id `);
|
|
113
|
+
stmt.run(userData);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
throw SqliteErrorToValidationError(e, userData)
|
|
116
|
+
}
|
|
117
|
+
return this.findById(id)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async delete(id: UUID): Promise<boolean> {
|
|
121
|
+
const stmt = this.db.prepare('DELETE FROM users WHERE id = ?');
|
|
122
|
+
stmt.run(id);
|
|
123
|
+
return true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async deleteAll(): Promise<boolean> {
|
|
127
|
+
const stmt = this.db.prepare('DELETE FROM users');
|
|
128
|
+
stmt.run();
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async findById(id: UUID): Promise<IUser> {
|
|
133
|
+
const user = this.db.prepare('SELECT * FROM users WHERE id = ?').get(id);
|
|
134
|
+
if (!user) {
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
user.role = await this.findRoleById(user.role)
|
|
138
|
+
return user
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async findByUsername(username: string): Promise<IUser> {
|
|
142
|
+
const user = this.db.prepare('SELECT * FROM users WHERE username = ?').get(username);
|
|
143
|
+
if (!user) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
user.role = await this.findRoleById(user.role)
|
|
147
|
+
return user
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async paginate(page: number = 1, limit: number = 5, search?: string): Promise<IPaginateResult> {
|
|
151
|
+
|
|
152
|
+
const offset = page > 1 ? (page - 1) * limit : 0
|
|
153
|
+
|
|
154
|
+
let where
|
|
155
|
+
if (search) {
|
|
156
|
+
where = ` WHERE name LIKE '%${search}%' OR username LIKE '%${search}%'`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const rCount = this.db.prepare('SELECT COUNT(*) as count FROM users' + where).get();
|
|
160
|
+
const users = this.db.prepare('SELECT * FROM users LIMIT ? OFFSET ?' + where).all([limit, offset]);
|
|
161
|
+
|
|
162
|
+
for (const user of users) {
|
|
163
|
+
let role = await this.findRoleById(user.role)
|
|
164
|
+
if (role) {
|
|
165
|
+
user.role = role
|
|
166
|
+
} else {
|
|
167
|
+
user.role = null
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
user.active = user.active === 1
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
page: page,
|
|
175
|
+
limit: limit,
|
|
176
|
+
total: rCount.count,
|
|
177
|
+
items: users
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async findRoleById(id: IID) {
|
|
182
|
+
return await this.roleRepository.findById(id)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async changePassword(id: IID, password: string): Promise<boolean> {
|
|
186
|
+
const stmt = this.db.prepare(`UPDATE users
|
|
187
|
+
SET password = @password
|
|
188
|
+
WHERE id = @id `);
|
|
189
|
+
stmt.run({id: id, password: password});
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export default UserSqliteRepository
|