diff --git a/.gitignore b/.gitignore index 4d0f1d4b..2ef4b859 100644 --- a/.gitignore +++ b/.gitignore @@ -47,11 +47,9 @@ dist/** !.yarn/sdks !.yarn/versions -# OSX -.DS_Store - # Google Services (e.g. APIs or Firebase) google-services.json GoogleService-Info.plist -tsconfig.build.tsbuildinfo \ No newline at end of file +tsconfig.build.tsbuildinfo +/.idea/* diff --git a/README.md b/README.md index d3512128..701ad026 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,14 @@ pnpm install docker-compose up ``` + +### Docker +workspace: nestjs + +```bash +$ cd resources +$ docker compose up +``` ### Env setup Example env file change name to .env ``` @@ -27,4 +35,4 @@ Example env file change name to .env ```properties pnpm db:up // migrations db pnpm dev -``` \ No newline at end of file +``` diff --git a/apps/nestjs/.env.example b/apps/nestjs/.env.example index bf368ce2..7d6ffec4 100644 --- a/apps/nestjs/.env.example +++ b/apps/nestjs/.env.example @@ -44,3 +44,5 @@ REDIS_CACHE_TTL=600 MAX_FILE_SIZE=5242880 MAX_FILES=5 PLAYGROUND=true + +FCM_JSON= diff --git a/apps/nestjs/migrations/.snapshot-framework.json b/apps/nestjs/migrations/.snapshot-postgres.json similarity index 60% rename from apps/nestjs/migrations/.snapshot-framework.json rename to apps/nestjs/migrations/.snapshot-postgres.json index 86e9dbcd..fcb36b38 100644 --- a/apps/nestjs/migrations/.snapshot-framework.json +++ b/apps/nestjs/migrations/.snapshot-postgres.json @@ -4,6 +4,133 @@ ], "name": "public", "tables": [ + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "name": { + "name": "name", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + } + }, + "name": "subject", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "subject_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "subject_name_unique", + "columnNames": [ + "name" + ], + "composite": false, + "primary": false, + "unique": true + }, + { + "keyName": "subject_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, { "columns": { "id": { @@ -108,7 +235,854 @@ "mappedType": "string" } }, - "name": "site_theme", + "name": "site_theme", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "site_theme_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "site_theme_name_unique", + "columnNames": [ + "name" + ], + "composite": false, + "primary": false, + "unique": true + }, + { + "keyName": "site_theme_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "name": { + "name": "name", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "name_en": { + "name": "name_en", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 200, + "mappedType": "string" + }, + "code": { + "name": "code", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + } + }, + "name": "site_language", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "site_language_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "site_language_name_unique", + "columnNames": [ + "name" + ], + "composite": false, + "primary": false, + "unique": true + }, + { + "keyName": "site_language_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "name": { + "name": "name", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "description": { + "name": "description", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + } + }, + "name": "role", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "role_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "role_name_unique", + "columnNames": [ + "name" + ], + "composite": false, + "primary": false, + "unique": true + }, + { + "keyName": "role_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "subject_id": { + "name": "subject_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "uuid" + }, + "action": { + "name": "action", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "enumItems": [ + "manage", + "create", + "read", + "update", + "delete" + ], + "mappedType": "enum" + } + }, + "name": "permission", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "permission_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "permission_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "permission_subject_id_foreign": { + "constraintName": "permission_subject_id_foreign", + "columnNames": [ + "subject_id" + ], + "localTableName": "public.permission", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.subject", + "deleteRule": "set null", + "updateRule": "cascade" + } + } + }, + { + "columns": { + "role_id": { + "name": "role_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "uuid" + }, + "permission_id": { + "name": "permission_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "uuid" + } + }, + "name": "role_permissions", + "schema": "public", + "indexes": [ + { + "keyName": "role_permissions_pkey", + "columnNames": [ + "role_id", + "permission_id" + ], + "composite": true, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "role_permissions_role_id_foreign": { + "constraintName": "role_permissions_role_id_foreign", + "columnNames": [ + "role_id" + ], + "localTableName": "public.role_permissions", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.role", + "deleteRule": "cascade", + "updateRule": "cascade" + }, + "role_permissions_permission_id_foreign": { + "constraintName": "role_permissions_permission_id_foreign", + "columnNames": [ + "permission_id" + ], + "localTableName": "public.role_permissions", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.permission", + "deleteRule": "cascade", + "updateRule": "cascade" + } + } + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "name": { + "name": "name", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "url": { + "name": "url", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "false", + "mappedType": "boolean" + }, + "is_sql": { + "name": "is_sql", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "false", + "mappedType": "boolean" + }, + "is_organization": { + "name": "is_organization", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "false", + "mappedType": "boolean" + }, + "sql": { + "name": "sql", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "text" + }, + "columns": { + "name": "columns", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "text" + } + }, + "name": "page", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "page_created_at_index", + "primary": false, + "unique": false + }, + { + "keyName": "page_name_unique", + "columnNames": [ + "name" + ], + "composite": false, + "primary": false, + "unique": true + }, + { + "keyName": "page_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "name": { + "name": "name", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "icon": { + "name": "icon", + "type": "varchar(200)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 200, + "mappedType": "string" + }, + "order": { + "name": "order", + "type": "int", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "0", + "mappedType": "integer" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "false", + "mappedType": "boolean" + }, + "parent_menu_id": { + "name": "parent_menu_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "uuid" + } + }, + "name": "menu", "schema": "public", "indexes": [ { @@ -116,12 +1090,12 @@ "created_at" ], "composite": false, - "keyName": "site_theme_created_at_index", + "keyName": "menu_created_at_index", "primary": false, "unique": false }, { - "keyName": "site_theme_name_unique", + "keyName": "menu_name_unique", "columnNames": [ "name" ], @@ -130,7 +1104,7 @@ "unique": true }, { - "keyName": "site_theme_pkey", + "keyName": "menu_pkey", "columnNames": [ "id" ], @@ -140,7 +1114,21 @@ } ], "checks": [], - "foreignKeys": {} + "foreignKeys": { + "menu_parent_menu_id_foreign": { + "constraintName": "menu_parent_menu_id_foreign", + "columnNames": [ + "parent_menu_id" + ], + "localTableName": "public.menu", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.menu", + "deleteRule": "set null", + "updateRule": "cascade" + } + } }, { "columns": { @@ -224,38 +1212,179 @@ "default": "false", "mappedType": "boolean" }, - "name": { - "name": "name", - "type": "varchar(200)", + "menu_id": { + "name": "menu_id", + "type": "uuid", "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, - "length": 200, - "mappedType": "string" + "nullable": true, + "mappedType": "uuid" }, - "name_en": { - "name": "name_en", - "type": "varchar(200)", + "page_id": { + "name": "page_id", + "type": "uuid", "unsigned": false, "autoincrement": false, "primary": false, "nullable": true, - "length": 200, - "mappedType": "string" + "mappedType": "uuid" + } + }, + "name": "pageMenu", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "created_at" + ], + "composite": false, + "keyName": "pageMenu_created_at_index", + "primary": false, + "unique": false }, - "code": { - "name": "code", - "type": "varchar(200)", + { + "keyName": "pageMenu_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "pageMenu_menu_id_foreign": { + "constraintName": "pageMenu_menu_id_foreign", + "columnNames": [ + "menu_id" + ], + "localTableName": "public.pageMenu", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.menu", + "deleteRule": "CASCADE", + "updateRule": "cascade" + }, + "pageMenu_page_id_foreign": { + "constraintName": "pageMenu_page_id_foreign", + "columnNames": [ + "page_id" + ], + "localTableName": "public.pageMenu", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.page", + "deleteRule": "CASCADE", + "updateRule": "cascade" + } + } + }, + { + "columns": { + "id": { + "name": "id", + "type": "uuid", "unsigned": false, "autoincrement": false, "primary": false, "nullable": false, - "length": 200, - "mappedType": "string" + "default": "uuid_generate_v4()", + "mappedType": "uuid" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz(0)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "hidden": { + "name": "hidden", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "disabled": { + "name": "disabled", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "archived": { + "name": "archived", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "default": "false", + "mappedType": "boolean" + }, + "page_menu_id": { + "name": "page_menu_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "uuid" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "uuid" } }, - "name": "site_language", + "name": "pageMenuRole", "schema": "public", "indexes": [ { @@ -263,21 +1392,22 @@ "created_at" ], "composite": false, - "keyName": "site_language_created_at_index", + "keyName": "pageMenuRole_created_at_index", "primary": false, "unique": false }, { - "keyName": "site_language_name_unique", + "keyName": "pageMenuRole_page_menu_id_role_id_unique", "columnNames": [ - "name" + "page_menu_id", + "role_id" ], - "composite": false, + "composite": true, "primary": false, "unique": true }, { - "keyName": "site_language_pkey", + "keyName": "pageMenuRole_pkey", "columnNames": [ "id" ], @@ -287,7 +1417,34 @@ } ], "checks": [], - "foreignKeys": {} + "foreignKeys": { + "pageMenuRole_page_menu_id_foreign": { + "constraintName": "pageMenuRole_page_menu_id_foreign", + "columnNames": [ + "page_menu_id" + ], + "localTableName": "public.pageMenuRole", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.pageMenu", + "deleteRule": "CASCADE", + "updateRule": "cascade" + }, + "pageMenuRole_role_id_foreign": { + "constraintName": "pageMenuRole_role_id_foreign", + "columnNames": [ + "role_id" + ], + "localTableName": "public.pageMenuRole", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.role", + "deleteRule": "set null", + "updateRule": "cascade" + } + } }, { "columns": { @@ -1016,12 +2173,12 @@ }, "name": { "name": "name", - "type": "bytea", + "type": "varchar(255)", "unsigned": false, "autoincrement": false, "primary": false, - "nullable": true, - "mappedType": "blob" + "nullable": false, + "mappedType": "string" }, "email": { "name": "email", @@ -1029,7 +2186,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": true, + "nullable": false, "mappedType": "string" }, "email_verified": { @@ -1287,6 +2444,16 @@ "length": 200, "mappedType": "string" }, + "device_id": { + "name": "device_id", + "type": "varchar(500)", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 500, + "mappedType": "string" + }, "created_by_id": { "name": "created_by_id", "type": "uuid", @@ -1844,6 +3011,71 @@ "updateRule": "cascade" } } + }, + { + "columns": { + "user_id": { + "name": "user_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "uuid" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "uuid" + } + }, + "name": "user_roles", + "schema": "public", + "indexes": [ + { + "keyName": "user_roles_pkey", + "columnNames": [ + "user_id", + "role_id" + ], + "composite": true, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "user_roles_user_id_foreign": { + "constraintName": "user_roles_user_id_foreign", + "columnNames": [ + "user_id" + ], + "localTableName": "public.user_roles", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.user", + "deleteRule": "cascade", + "updateRule": "cascade" + }, + "user_roles_role_id_foreign": { + "constraintName": "user_roles_role_id_foreign", + "columnNames": [ + "role_id" + ], + "localTableName": "public.user_roles", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.role", + "deleteRule": "cascade", + "updateRule": "cascade" + } + } } ] } diff --git a/apps/nestjs/migrations/Migration20220623062556.ts b/apps/nestjs/migrations/Migration20220623062556.ts deleted file mode 100644 index d199c113..00000000 --- a/apps/nestjs/migrations/Migration20220623062556.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Migration } from '@mikro-orm/migrations'; - -export class Migration20220623062556 extends Migration { - - async up(): Promise { - this.addSql('create table "site_theme" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "code" varchar(200) not null default \'t\');'); - this.addSql('create index "site_theme_created_at_index" on "site_theme" ("created_at");'); - this.addSql('alter table "site_theme" add constraint "site_theme_name_unique" unique ("name");'); - this.addSql('alter table "site_theme" add constraint "site_theme_pkey" primary key ("id");'); - - this.addSql('create table "site_language" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "name_en" varchar(200) null, "code" varchar(200) not null);'); - this.addSql('create index "site_language_created_at_index" on "site_language" ("created_at");'); - this.addSql('alter table "site_language" add constraint "site_language_name_unique" unique ("name");'); - this.addSql('alter table "site_language" add constraint "site_language_pkey" primary key ("id");'); - - this.addSql('create table "currency" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "code" varchar(200) not null);'); - this.addSql('create index "currency_created_at_index" on "currency" ("created_at");'); - this.addSql('alter table "currency" add constraint "currency_name_unique" unique ("name");'); - this.addSql('alter table "currency" add constraint "currency_pkey" primary key ("id");'); - - this.addSql('create table "country" ("id" serial primary key, "name" varchar(200) null, "iso3" varchar(200) null, "iso2" varchar(200) null, "numeric_code" varchar(200) null, "phone_code" varchar(200) null, "capital" varchar(200) null, "currency" varchar(200) null, "currency_name" varchar(200) null, "currency_symbol" varchar(200) null, "tld" varchar(100) null, "native" varchar(200) null, "region" varchar(200) null, "sub_region" varchar(200) null, "time_zones" varchar(20000) null, "latitude" varchar(200) null, "longitude" varchar(200) null, "emoji" varchar(200) null, "emoji_u" varchar(200) null);'); - this.addSql('alter table "country" add constraint "country_name_unique" unique ("name");'); - - this.addSql('create table "state" ("id" serial primary key, "name" varchar(200) not null, "country_code" varchar(200) not null, "country_name" varchar(200) not null, "state_code" varchar(200) not null, "type" varchar(200) null, "latitude" varchar(200) not null, "longitude" varchar(200) not null, "country_id" int not null);'); - - this.addSql('create table "city" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "country_id" int null);'); - this.addSql('create index "city_created_at_index" on "city" ("created_at");'); - this.addSql('alter table "city" add constraint "city_name_country_id_unique" unique ("name", "country_id");'); - this.addSql('alter table "city" add constraint "city_pkey" primary key ("id");'); - - this.addSql('create table "user" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "provider_account_id" varchar(255) null, "username" varchar(255) not null, "name" bytea null, "email" varchar(255) null, "email_verified" timestamptz(0) null, "password" varchar(255) not null, "password_salt" varchar(255) not null, "display_name" varchar(255) not null, "image" varchar(255) null, "is_terms_accepted" boolean not null default false, "last_login_at" bigint null default null, "last_login_ip" varchar(255) null default null, "user_setting_id" uuid null);'); - this.addSql('comment on column "user"."last_login_at" is \'last login time\';'); - this.addSql('comment on column "user"."last_login_ip" is \'Last login IP\';'); - this.addSql('create index "user_created_at_index" on "user" ("created_at");'); - this.addSql('alter table "user" add constraint "user_provider_account_id_unique" unique ("provider_account_id");'); - this.addSql('alter table "user" add constraint "user_username_unique" unique ("username");'); - this.addSql('alter table "user" add constraint "user_email_unique" unique ("email");'); - this.addSql('alter table "user" add constraint "user_user_setting_id_unique" unique ("user_setting_id");'); - this.addSql('alter table "user" add constraint "user_pkey" primary key ("id");'); - - this.addSql('create table "notification_token" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "token" varchar(200) not null, "created_by_id" uuid not null);'); - this.addSql('create index "notification_token_created_at_index" on "notification_token" ("created_at");'); - this.addSql('alter table "notification_token" add constraint "notification_token_token_unique" unique ("token");'); - this.addSql('alter table "notification_token" add constraint "notification_token_pkey" primary key ("id");'); - - this.addSql('create table "user_device" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "fingerprint" varchar(255) not null, "token" varchar(255) null, "name" varchar(255) not null, "type" varchar(255) not null, "user_agent" jsonb not null, "last_login_at" bigint null, "last_login_ip" varchar(255) null, "first_login_at" bigint null, "first_login_ip" varchar(255) null, "user_id" uuid not null);'); - this.addSql('comment on column "user_device"."fingerprint" is \'device fingerprint\';'); - this.addSql('comment on column "user_device"."token" is \'login token jit\';'); - this.addSql('comment on column "user_device"."name" is \'device name\';'); - this.addSql('comment on column "user_device"."type" is \'Equipment type\';'); - this.addSql('comment on column "user_device"."user_agent" is \'device UA\';'); - this.addSql('comment on column "user_device"."last_login_at" is \'last login time\';'); - this.addSql('comment on column "user_device"."last_login_ip" is \'Last login IP\';'); - this.addSql('comment on column "user_device"."first_login_at" is \'Device first login time\';'); - this.addSql('comment on column "user_device"."first_login_ip" is \'Device first login IP\';'); - this.addSql('create index "user_device_created_at_index" on "user_device" ("created_at");'); - this.addSql('alter table "user_device" add constraint "user_device_pkey" primary key ("id");'); - - this.addSql('create table "user_setting" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "timezone" varchar(200) null, "user_id" uuid not null, "site_language_id" uuid null, "site_theme_id" uuid null, "country_id" int null, "city_id" uuid null, "currency_id" uuid null, "state_id" int null);'); - this.addSql('create index "user_setting_created_at_index" on "user_setting" ("created_at");'); - this.addSql('alter table "user_setting" add constraint "user_setting_user_id_unique" unique ("user_id");'); - this.addSql('alter table "user_setting" add constraint "user_setting_pkey" primary key ("id");'); - - this.addSql('alter table "state" add constraint "state_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade;'); - - this.addSql('alter table "city" add constraint "city_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade on delete set null;'); - - this.addSql('alter table "user" add constraint "user_user_setting_id_foreign" foreign key ("user_setting_id") references "user_setting" ("id") on update cascade on delete set null;'); - - this.addSql('alter table "notification_token" add constraint "notification_token_created_by_id_foreign" foreign key ("created_by_id") references "user" ("id") on update cascade;'); - - this.addSql('alter table "user_device" add constraint "user_device_user_id_foreign" foreign key ("user_id") references "user" ("id") on update cascade;'); - - this.addSql('alter table "user_setting" add constraint "user_setting_user_id_foreign" foreign key ("user_id") references "user" ("id") on update cascade;'); - this.addSql('alter table "user_setting" add constraint "user_setting_site_language_id_foreign" foreign key ("site_language_id") references "site_language" ("id") on update cascade on delete set null;'); - this.addSql('alter table "user_setting" add constraint "user_setting_site_theme_id_foreign" foreign key ("site_theme_id") references "site_theme" ("id") on update cascade on delete set null;'); - this.addSql('alter table "user_setting" add constraint "user_setting_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade on delete set null;'); - this.addSql('alter table "user_setting" add constraint "user_setting_city_id_foreign" foreign key ("city_id") references "city" ("id") on update cascade on delete set null;'); - this.addSql('alter table "user_setting" add constraint "user_setting_currency_id_foreign" foreign key ("currency_id") references "currency" ("id") on update cascade on delete set null;'); - this.addSql('alter table "user_setting" add constraint "user_setting_state_id_foreign" foreign key ("state_id") references "state" ("id") on update cascade on delete set null;'); - } - -} diff --git a/apps/nestjs/migrations/Migration20220806100337.ts b/apps/nestjs/migrations/Migration20220806100337.ts new file mode 100644 index 00000000..25d1241e --- /dev/null +++ b/apps/nestjs/migrations/Migration20220806100337.ts @@ -0,0 +1,205 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20220806100337 extends Migration { + + async up(): Promise { + this.addSql('create table "subject" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, constraint "subject_pkey" primary key ("id"));'); + this.addSql('create index "subject_created_at_index" on "subject" ("created_at");'); + this.addSql('alter table "subject" add constraint "subject_name_unique" unique ("name");'); + + this.addSql('create table "site_theme" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "code" varchar(200) not null default \'t\', constraint "site_theme_pkey" primary key ("id"));'); + this.addSql('create index "site_theme_created_at_index" on "site_theme" ("created_at");'); + this.addSql('alter table "site_theme" add constraint "site_theme_name_unique" unique ("name");'); + + this.addSql('create table "site_language" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "name_en" varchar(200) null, "code" varchar(200) not null, constraint "site_language_pkey" primary key ("id"));'); + this.addSql('create index "site_language_created_at_index" on "site_language" ("created_at");'); + this.addSql('alter table "site_language" add constraint "site_language_name_unique" unique ("name");'); + + this.addSql('create table "role" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "description" varchar(200) not null, constraint "role_pkey" primary key ("id"));'); + this.addSql('create index "role_created_at_index" on "role" ("created_at");'); + this.addSql('alter table "role" add constraint "role_name_unique" unique ("name");'); + + this.addSql('create table "permission" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "subject_id" uuid null, "action" text check ("action" in (\'manage\', \'create\', \'read\', \'update\', \'delete\')) not null, constraint "permission_pkey" primary key ("id"));'); + this.addSql('create index "permission_created_at_index" on "permission" ("created_at");'); + + this.addSql('create table "role_permissions" ("role_id" uuid not null, "permission_id" uuid not null, constraint "role_permissions_pkey" primary key ("role_id", "permission_id"));'); + + this.addSql('create table "page" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "url" varchar(200) not null, "is_public" boolean not null default false, "is_sql" boolean not null default false, "is_organization" boolean not null default false, "sql" text null, "columns" text null, constraint "page_pkey" primary key ("id"));'); + this.addSql('create index "page_created_at_index" on "page" ("created_at");'); + this.addSql('alter table "page" add constraint "page_name_unique" unique ("name");'); + + this.addSql('create table "menu" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "icon" varchar(200) not null, "order" int not null default 0, "is_active" boolean not null default false, "parent_menu_id" uuid null, constraint "menu_pkey" primary key ("id"));'); + this.addSql('create index "menu_created_at_index" on "menu" ("created_at");'); + this.addSql('alter table "menu" add constraint "menu_name_unique" unique ("name");'); + + this.addSql('create table "pageMenu" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "menu_id" uuid null, "page_id" uuid null, constraint "pageMenu_pkey" primary key ("id"));'); + this.addSql('create index "pageMenu_created_at_index" on "pageMenu" ("created_at");'); + + this.addSql('create table "pageMenuRole" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "page_menu_id" uuid null, "role_id" uuid null, constraint "pageMenuRole_pkey" primary key ("id"));'); + this.addSql('create index "pageMenuRole_created_at_index" on "pageMenuRole" ("created_at");'); + this.addSql('alter table "pageMenuRole" add constraint "pageMenuRole_page_menu_id_role_id_unique" unique ("page_menu_id", "role_id");'); + + this.addSql('create table "currency" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "code" varchar(200) not null, constraint "currency_pkey" primary key ("id"));'); + this.addSql('create index "currency_created_at_index" on "currency" ("created_at");'); + this.addSql('alter table "currency" add constraint "currency_name_unique" unique ("name");'); + + this.addSql('create table "country" ("id" serial primary key, "name" varchar(200) null, "iso3" varchar(200) null, "iso2" varchar(200) null, "numeric_code" varchar(200) null, "phone_code" varchar(200) null, "capital" varchar(200) null, "currency" varchar(200) null, "currency_name" varchar(200) null, "currency_symbol" varchar(200) null, "tld" varchar(100) null, "native" varchar(200) null, "region" varchar(200) null, "sub_region" varchar(200) null, "time_zones" varchar(20000) null, "latitude" varchar(200) null, "longitude" varchar(200) null, "emoji" varchar(200) null, "emoji_u" varchar(200) null);'); + this.addSql('alter table "country" add constraint "country_name_unique" unique ("name");'); + + this.addSql('create table "state" ("id" serial primary key, "name" varchar(200) not null, "country_code" varchar(200) not null, "country_name" varchar(200) not null, "state_code" varchar(200) not null, "type" varchar(200) null, "latitude" varchar(200) not null, "longitude" varchar(200) not null, "country_id" int not null);'); + + this.addSql('create table "city" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "name" varchar(200) not null, "country_id" int null, constraint "city_pkey" primary key ("id"));'); + this.addSql('create index "city_created_at_index" on "city" ("created_at");'); + this.addSql('alter table "city" add constraint "city_name_country_id_unique" unique ("name", "country_id");'); + + this.addSql('create table "user" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "provider_account_id" varchar(255) null, "username" varchar(255) not null, "name" varchar(255) not null, "email" varchar(255) not null, "email_verified" timestamptz(0) null, "password" varchar(255) not null, "password_salt" varchar(255) not null, "display_name" varchar(255) not null, "image" varchar(255) null, "is_terms_accepted" boolean not null default false, "last_login_at" bigint null default null, "last_login_ip" varchar(255) null default null, "user_setting_id" uuid null, constraint "user_pkey" primary key ("id"));'); + this.addSql('comment on column "user"."last_login_at" is \'last login time\';'); + this.addSql('comment on column "user"."last_login_ip" is \'Last login IP\';'); + this.addSql('create index "user_created_at_index" on "user" ("created_at");'); + this.addSql('alter table "user" add constraint "user_provider_account_id_unique" unique ("provider_account_id");'); + this.addSql('alter table "user" add constraint "user_username_unique" unique ("username");'); + this.addSql('alter table "user" add constraint "user_email_unique" unique ("email");'); + this.addSql('alter table "user" add constraint "user_user_setting_id_unique" unique ("user_setting_id");'); + + this.addSql('create table "notification_token" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "token" varchar(200) not null, "device_id" varchar(500) not null, "created_by_id" uuid not null, constraint "notification_token_pkey" primary key ("id"));'); + this.addSql('create index "notification_token_created_at_index" on "notification_token" ("created_at");'); + this.addSql('alter table "notification_token" add constraint "notification_token_token_unique" unique ("token");'); + + this.addSql('create table "user_device" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "fingerprint" varchar(255) not null, "token" varchar(255) null, "name" varchar(255) not null, "type" varchar(255) not null, "user_agent" jsonb not null, "last_login_at" bigint null, "last_login_ip" varchar(255) null, "first_login_at" bigint null, "first_login_ip" varchar(255) null, "user_id" uuid not null, constraint "user_device_pkey" primary key ("id"));'); + this.addSql('comment on column "user_device"."fingerprint" is \'device fingerprint\';'); + this.addSql('comment on column "user_device"."token" is \'login token jit\';'); + this.addSql('comment on column "user_device"."name" is \'device name\';'); + this.addSql('comment on column "user_device"."type" is \'Equipment type\';'); + this.addSql('comment on column "user_device"."user_agent" is \'device UA\';'); + this.addSql('comment on column "user_device"."last_login_at" is \'last login time\';'); + this.addSql('comment on column "user_device"."last_login_ip" is \'Last login IP\';'); + this.addSql('comment on column "user_device"."first_login_at" is \'Device first login time\';'); + this.addSql('comment on column "user_device"."first_login_ip" is \'Device first login IP\';'); + this.addSql('create index "user_device_created_at_index" on "user_device" ("created_at");'); + + this.addSql('create table "user_setting" ("id" uuid not null default uuid_generate_v4(), "created_at" timestamptz(0) null, "updated_at" timestamptz(0) null, "deleted_at" timestamptz(0) null, "deleted" boolean null default false, "hidden" boolean null default false, "disabled" boolean null default false, "archived" boolean null default false, "timezone" varchar(200) null, "user_id" uuid not null, "site_language_id" uuid null, "site_theme_id" uuid null, "country_id" int null, "city_id" uuid null, "currency_id" uuid null, "state_id" int null, constraint "user_setting_pkey" primary key ("id"));'); + this.addSql('create index "user_setting_created_at_index" on "user_setting" ("created_at");'); + this.addSql('alter table "user_setting" add constraint "user_setting_user_id_unique" unique ("user_id");'); + + this.addSql('create table "user_roles" ("user_id" uuid not null, "role_id" uuid not null, constraint "user_roles_pkey" primary key ("user_id", "role_id"));'); + + this.addSql('alter table "permission" add constraint "permission_subject_id_foreign" foreign key ("subject_id") references "subject" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "role_permissions" add constraint "role_permissions_role_id_foreign" foreign key ("role_id") references "role" ("id") on update cascade on delete cascade;'); + this.addSql('alter table "role_permissions" add constraint "role_permissions_permission_id_foreign" foreign key ("permission_id") references "permission" ("id") on update cascade on delete cascade;'); + + this.addSql('alter table "menu" add constraint "menu_parent_menu_id_foreign" foreign key ("parent_menu_id") references "menu" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "pageMenu" add constraint "pageMenu_menu_id_foreign" foreign key ("menu_id") references "menu" ("id") on update cascade on delete CASCADE;'); + this.addSql('alter table "pageMenu" add constraint "pageMenu_page_id_foreign" foreign key ("page_id") references "page" ("id") on update cascade on delete CASCADE;'); + + this.addSql('alter table "pageMenuRole" add constraint "pageMenuRole_page_menu_id_foreign" foreign key ("page_menu_id") references "pageMenu" ("id") on update cascade on delete CASCADE;'); + this.addSql('alter table "pageMenuRole" add constraint "pageMenuRole_role_id_foreign" foreign key ("role_id") references "role" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "state" add constraint "state_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade;'); + + this.addSql('alter table "city" add constraint "city_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "user" add constraint "user_user_setting_id_foreign" foreign key ("user_setting_id") references "user_setting" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "notification_token" add constraint "notification_token_created_by_id_foreign" foreign key ("created_by_id") references "user" ("id") on update cascade;'); + + this.addSql('alter table "user_device" add constraint "user_device_user_id_foreign" foreign key ("user_id") references "user" ("id") on update cascade;'); + + this.addSql('alter table "user_setting" add constraint "user_setting_user_id_foreign" foreign key ("user_id") references "user" ("id") on update cascade;'); + this.addSql('alter table "user_setting" add constraint "user_setting_site_language_id_foreign" foreign key ("site_language_id") references "site_language" ("id") on update cascade on delete set null;'); + this.addSql('alter table "user_setting" add constraint "user_setting_site_theme_id_foreign" foreign key ("site_theme_id") references "site_theme" ("id") on update cascade on delete set null;'); + this.addSql('alter table "user_setting" add constraint "user_setting_country_id_foreign" foreign key ("country_id") references "country" ("id") on update cascade on delete set null;'); + this.addSql('alter table "user_setting" add constraint "user_setting_city_id_foreign" foreign key ("city_id") references "city" ("id") on update cascade on delete set null;'); + this.addSql('alter table "user_setting" add constraint "user_setting_currency_id_foreign" foreign key ("currency_id") references "currency" ("id") on update cascade on delete set null;'); + this.addSql('alter table "user_setting" add constraint "user_setting_state_id_foreign" foreign key ("state_id") references "state" ("id") on update cascade on delete set null;'); + + this.addSql('alter table "user_roles" add constraint "user_roles_user_id_foreign" foreign key ("user_id") references "user" ("id") on update cascade on delete cascade;'); + this.addSql('alter table "user_roles" add constraint "user_roles_role_id_foreign" foreign key ("role_id") references "role" ("id") on update cascade on delete cascade;'); + } + + async down(): Promise { + this.addSql('alter table "permission" drop constraint "permission_subject_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_site_theme_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_site_language_id_foreign";'); + + this.addSql('alter table "role_permissions" drop constraint "role_permissions_role_id_foreign";'); + + this.addSql('alter table "pageMenuRole" drop constraint "pageMenuRole_role_id_foreign";'); + + this.addSql('alter table "user_roles" drop constraint "user_roles_role_id_foreign";'); + + this.addSql('alter table "role_permissions" drop constraint "role_permissions_permission_id_foreign";'); + + this.addSql('alter table "pageMenu" drop constraint "pageMenu_page_id_foreign";'); + + this.addSql('alter table "menu" drop constraint "menu_parent_menu_id_foreign";'); + + this.addSql('alter table "pageMenu" drop constraint "pageMenu_menu_id_foreign";'); + + this.addSql('alter table "pageMenuRole" drop constraint "pageMenuRole_page_menu_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_currency_id_foreign";'); + + this.addSql('alter table "state" drop constraint "state_country_id_foreign";'); + + this.addSql('alter table "city" drop constraint "city_country_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_country_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_state_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_city_id_foreign";'); + + this.addSql('alter table "notification_token" drop constraint "notification_token_created_by_id_foreign";'); + + this.addSql('alter table "user_device" drop constraint "user_device_user_id_foreign";'); + + this.addSql('alter table "user_setting" drop constraint "user_setting_user_id_foreign";'); + + this.addSql('alter table "user_roles" drop constraint "user_roles_user_id_foreign";'); + + this.addSql('alter table "user" drop constraint "user_user_setting_id_foreign";'); + + this.addSql('drop table if exists "subject" cascade;'); + + this.addSql('drop table if exists "site_theme" cascade;'); + + this.addSql('drop table if exists "site_language" cascade;'); + + this.addSql('drop table if exists "role" cascade;'); + + this.addSql('drop table if exists "permission" cascade;'); + + this.addSql('drop table if exists "role_permissions" cascade;'); + + this.addSql('drop table if exists "page" cascade;'); + + this.addSql('drop table if exists "menu" cascade;'); + + this.addSql('drop table if exists "pageMenu" cascade;'); + + this.addSql('drop table if exists "pageMenuRole" cascade;'); + + this.addSql('drop table if exists "currency" cascade;'); + + this.addSql('drop table if exists "country" cascade;'); + + this.addSql('drop table if exists "state" cascade;'); + + this.addSql('drop table if exists "city" cascade;'); + + this.addSql('drop table if exists "user" cascade;'); + + this.addSql('drop table if exists "notification_token" cascade;'); + + this.addSql('drop table if exists "user_device" cascade;'); + + this.addSql('drop table if exists "user_setting" cascade;'); + + this.addSql('drop table if exists "user_roles" cascade;'); + } + +} diff --git a/apps/nestjs/package.json b/apps/nestjs/package.json index ae941c33..95ebd66f 100644 --- a/apps/nestjs/package.json +++ b/apps/nestjs/package.json @@ -22,14 +22,17 @@ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "migrate:init": "mikro-orm migration:create --initial", "migrate:create": "npx mikro-orm migration:create", "migrate:update": "npx mikro-orm schema:update --run", - "migrate:push": "npx mikro-orm migration:up", - "migrate:drop": "npx mikro-orm schema:fresh --run" + "migrate:up": "npx mikro-orm migration:up", + "migrate:down": "npx mikro-orm migration:down", + "migrate:fresh": "npx mikro-orm schema:fresh --run", + "seeder:run": "mikro-orm seeder:run --class=DatabaseSeeder" }, "dependencies": { "@aws-sdk/client-s3": "3.135.0", - "@casl/ability": "5.4.4", + "@casl/ability": "6.0.0", "@liaoliaots/nestjs-redis": "9.0.1", "@mikro-orm/cli": "5.2.4", "@mikro-orm/core": "5.2.4", diff --git a/docker-compose.yml b/apps/nestjs/resources/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to apps/nestjs/resources/docker-compose.yml diff --git a/apps/nestjs/resources/initdb/extensions.sh b/apps/nestjs/resources/initdb/extensions.sh new file mode 100644 index 00000000..bfb71303 --- /dev/null +++ b/apps/nestjs/resources/initdb/extensions.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e +set -u + +echo "EXTENSIONS CREATING" + +function create_extensions() { + psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" <<-EOSQL + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +EOSQL +} + +create_extensions + +echo "EXTENSIONS CREATED SUCCESSFULLY" diff --git a/apps/nestjs/schema.graphql b/apps/nestjs/schema.graphql index d45bb979..f4a38667 100644 --- a/apps/nestjs/schema.graphql +++ b/apps/nestjs/schema.graphql @@ -2,6 +2,24 @@ # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) # ------------------------------------------------------ +enum ActionEnum { + CREATE + DELETE + MANAGE + READ + UPDATE +} + +input AddPermissionInput { + permissionId: String! + roleId: String! +} + +input AddRoleInput { + roleId: String! + userId: String! +} + type City { archived: Boolean! country: Country @@ -41,6 +59,11 @@ type Country { userSettings: [UserSetting!]! } +input CreateAdminRoleInput { + description: String! + name: String! +} + input CreateNotificationTokenInput { """Example field (placeholder)""" deviceId: String! @@ -49,6 +72,15 @@ input CreateNotificationTokenInput { token: String! } +input CreatePermissionInput { + action: ActionEnum! + subjectId: ID! +} + +input CreateSubjectInput { + name: String! +} + type Currency { archived: Boolean! code: String! @@ -105,15 +137,28 @@ type Menu { } type Mutation { + addPermissionToRole(addPermission: AddPermissionInput!): Role! + addRoleToUser(addRole: AddRoleInput!): Role! + createAdminRole(data: CreateAdminRoleInput!): Role! + createPermission(data: CreatePermissionInput!): Permission! + createSubject(data: CreateSubjectInput!): Subject! + deletePermission(id: String!): Permission! + deleteRole(id: String!): Role! + deleteSubject(id: String!): Subject! forgotPassword(data: ForgotPasswordInput!): Boolean! logout: Boolean! refreshToken(data: RefreshTokenInput!): TokenResponse! + removePermissionFromRole(removePermission: AddPermissionInput!): Role! + removeRoleFromUser(addRole: AddRoleInput!): Role! resetPassword(data: ResetPasswordInput!): Boolean! saveNotificationToken(data: CreateNotificationTokenInput!): NotificationToken! sendNotificationToken(data: SendNotificationInput!): Boolean! signIn(data: SignInInput!): TokenResponse! signUp(data: SignUpInput!): User! terminateSession(data: TerminateSessionInput!): Boolean! + updatePermission(data: UpdatePermissionInput!, id: String!): Permission! + updateRole(data: UpdateRoleInput!, id: String!): Role! + updateSubject(data: UpdateSubjectInput!, id: String!): Subject! } type NotificationToken { @@ -175,10 +220,31 @@ type PageMenuRole { updatedAt: DateTime } +type Permission { + action: ActionEnum! + archived: Boolean! + createdAt: DateTime! + deleted: Boolean! + deletedAt: DateTime + disabled: Boolean! + hidden: Boolean! + id: ID! + roles: [Role!]! + subject: Subject! + updatedAt: DateTime +} + type Query { example(data: ExampleInput!): String! me: User! + permissions: [Permission!]! + permiswork: String! + permiswork2: String! + role(name: String!): Role! + roles: [Role!]! sessions: [Session!]! + subject(name: String!): Subject! + subjects: [Subject!]! } input RefreshTokenInput { @@ -196,14 +262,15 @@ type Role { createdAt: DateTime! deleted: Boolean! deletedAt: DateTime + description: String! disabled: Boolean! hidden: Boolean! id: ID! name: String! - name_en: String pageMenus: [PageMenuRole!] + permissions: [Permission!] updatedAt: DateTime - userRoles: [UserRole!]! + users: [User!] } input SendNotificationInput { @@ -285,7 +352,7 @@ enum SiteThemeEnum { } type State { - country: [Country!]! + country: Country! countryCode: String! countryName: String! id: ID! @@ -297,6 +364,18 @@ type State { userSettings: [UserSetting!]! } +type Subject { + archived: Boolean! + createdAt: DateTime! + deleted: Boolean! + deletedAt: DateTime + disabled: Boolean! + hidden: Boolean! + id: ID! + name: String! + updatedAt: DateTime +} + input TerminateSessionInput { jti: String! } @@ -309,6 +388,20 @@ type TokenResponse { refreshToken: JWT! } +input UpdatePermissionInput { + action: ActionEnum + subjectId: ID +} + +input UpdateRoleInput { + description: String + name: String +} + +input UpdateSubjectInput { + name: String +} + type User { archived: Boolean! createdAt: DateTime! @@ -334,7 +427,7 @@ type User { image: String """Example field (placeholder)""" - is_terms_accepted: String! + isTermsAccepted: String! """last login time""" lastLoginAt: DateTime @@ -348,6 +441,7 @@ type User { """User notification tokens""" notificationToken: [NotificationToken!] providerAccountId: String + roles: [Role!] updatedAt: DateTime userSetting: UserSetting! @@ -448,19 +542,6 @@ type UserDevice { userAgent: UserAgent! } -type UserRole { - archived: Boolean! - createdAt: DateTime! - deleted: Boolean! - deletedAt: DateTime - disabled: Boolean! - hidden: Boolean! - id: ID! - role: Role! - updatedAt: DateTime - user: User! -} - type UserSetting { archived: Boolean! city: City diff --git a/apps/nestjs/seeders/factories/world.factory.ts b/apps/nestjs/seeders/factories/world.factory.ts index 519aaf28..a2c85c13 100644 --- a/apps/nestjs/seeders/factories/world.factory.ts +++ b/apps/nestjs/seeders/factories/world.factory.ts @@ -1,4 +1,4 @@ -import { EntityManager, Reference } from '@mikro-orm/core' +import { EntityManager } from '@mikro-orm/core' import { parse } from 'csv-parse/sync' import { getSeedCsv } from '@/core/utils' import { Country } from '@/entities' @@ -37,7 +37,7 @@ export interface Timezone { export interface IStates { id: string name: string - country_id: string + country_id: number country_code: string country_name: string state_code: string @@ -74,11 +74,7 @@ export class WorldFactory { id: Number(element.id), })) }) - await this.em.persistAndFlush(arrayData).catch((e) => { - // eslint-disable-next-line no-console - console.log(e) - }) - + this.em.persist(arrayData) const resultStates = getSeedCsv('states') const state = parse(resultStates, { trim: true, @@ -98,19 +94,11 @@ export class WorldFactory { name: element.name, type: element.type, }) - - const data2 = new Country() - data2.id = Number(element.country_id) - dataState.country = Reference.create(data2) - + const repo = this.em.getRepository(Country) + dataState.country = repo.getReference(element.country_id) states.push(dataState) }) - - await this.em.persistAndFlush(states) - // const knex = ( - // this.em.getConnection() as AbstractSqlConnection - // ).getKnex() - // // will get persisted automatically - // await knex.raw(result) + this.em.persist(states) + await this.em.flush() } } diff --git a/apps/nestjs/src/app.module.ts b/apps/nestjs/src/app.module.ts index 16255ac4..a31e6f9c 100644 --- a/apps/nestjs/src/app.module.ts +++ b/apps/nestjs/src/app.module.ts @@ -26,6 +26,7 @@ import { GqlConfigService } from './config/graphql.config' import { AuthResolver } from './app.resolver' import { TranslateModule } from './modules/translate/translate.module' import { IConfig } from './config/interfaces/config.interface' +import { CaslModule } from './modules/casl/casl.module' @Module({ imports: [ @@ -88,7 +89,7 @@ import { IConfig } from './config/interfaces/config.interface' return options }, }), - + CaslModule, ], controllers: [AppController], providers: [AppService, JwtService, AuthResolver, { diff --git a/apps/nestjs/src/app.resolver.ts b/apps/nestjs/src/app.resolver.ts index b81d8c36..aa2b5bbc 100644 --- a/apps/nestjs/src/app.resolver.ts +++ b/apps/nestjs/src/app.resolver.ts @@ -1,5 +1,9 @@ +import { UseGuards } from '@nestjs/common' import { Args, Field, InputType, Query, Resolver } from '@nestjs/graphql' import GraphQLJSON from 'graphql-type-json' +import { CheckPermissions } from './modules/casl/decorators/check-permissions.decorator' +import { PermissionsGuard } from './modules/casl/guards/permissions.guard' +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' @InputType() export class ExampleInput { @@ -15,5 +19,19 @@ export class AuthResolver { console.log(data) return 'test' } + + @UseGuards(PermissionsGuard) + @CheckPermissions([ActionEnum.CREATE, 'TODO']) + @Query(() => String) + async permiswork() { + return 'perm works' + } + + @UseGuards(PermissionsGuard) + @CheckPermissions([ActionEnum.DELETE, 'TODO']) + @Query(() => String) + async permiswork2() { + return 'perm works' + } } diff --git a/apps/nestjs/src/entities/index.ts b/apps/nestjs/src/entities/index.ts index cdbb1114..1997fc40 100644 --- a/apps/nestjs/src/entities/index.ts +++ b/apps/nestjs/src/entities/index.ts @@ -12,3 +12,5 @@ export { SiteTheme } from './site-theme.entity' export { User } from './user.entity' export { UserDeviceEntity } from './user-device.entity' export { NotificationToken } from './notification-token.entity' +export { Permission } from './permission.entity' +export { Subject } from './subject.entity' diff --git a/apps/nestjs/src/entities/permission.entity.ts b/apps/nestjs/src/entities/permission.entity.ts new file mode 100644 index 00000000..08449ca9 --- /dev/null +++ b/apps/nestjs/src/entities/permission.entity.ts @@ -0,0 +1,25 @@ +import { Collection, Entity, Enum, ManyToMany, ManyToOne } from '@mikro-orm/core' +import { Field, ObjectType } from '@nestjs/graphql' +import { BaseModel } from './base.model' +import { Role } from './role.entity' +import { Subject } from './subject.entity' +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' + +@Entity({ tableName: 'permission' }) +@ObjectType() +export class Permission extends BaseModel { + @ManyToOne({ + eager: true, + nullable: true, + }) + @Field(() => Subject) + subject = new Collection(this) + + @ManyToMany(() => Role, role => role.permissions) + @Field(() => [Role]) + roles = new Collection(this) + + @Enum(() => ActionEnum) + @Field(() => ActionEnum) + action!: ActionEnum +} diff --git a/apps/nestjs/src/entities/role.entity.ts b/apps/nestjs/src/entities/role.entity.ts index fdb89b75..63649c9f 100644 --- a/apps/nestjs/src/entities/role.entity.ts +++ b/apps/nestjs/src/entities/role.entity.ts @@ -1,9 +1,10 @@ -import { Collection, Entity, OneToMany, Property, Unique } from '@mikro-orm/core' +import { Collection, Entity, ManyToMany, OneToMany, Property, Unique } from '@mikro-orm/core' import { Field, ObjectType } from '@nestjs/graphql' import { BaseModel } from './base.model' -import { UserRole } from './user-role.entity' import { PageMenuRole } from './page-menu-role.entity' +import { User } from './user.entity' +import { Permission } from './permission.entity' @Entity({ tableName: 'role' }) @Unique({ properties: ['name'] }) @@ -13,20 +14,9 @@ export class Role extends BaseModel { @Field(() => String, { nullable: false }) name!: string - @Property({ type: 'varchar', length: 200, nullable: true }) - @Field(() => String, { nullable: true }) - name_en?: string | null - - // @OneToMany(() => UserRole, r => r.role, { - // eager: true, - // nullable: false, - // }) - // @Property({ type: 'json', nullable: true }) - // @Field(() => GraphQLJSON, { nullable: true }) - // permissions!: object - - @Field(() => [UserRole], { nullable: false }) - userRoles?: UserRole[] + @Property({ type: 'varchar', length: 200, nullable: false }) + @Field(() => String, { nullable: false }) + description!: string @OneToMany(() => PageMenuRole, p => p.role, { eager: true, @@ -35,4 +25,12 @@ export class Role extends BaseModel { }) @Field(() => [PageMenuRole], { nullable: true }) pageMenus = new Collection(this) + + @ManyToMany(() => User, user => user.roles) + @Field(() => [User], { nullable: true }) + users = new Collection(this) + + @ManyToMany(() => Permission, 'roles', { owner: true }) + @Field(() => [Permission], { nullable: true }) + permissions = new Collection(this) } diff --git a/apps/nestjs/src/entities/state.entity.ts b/apps/nestjs/src/entities/state.entity.ts index a156f029..2fe3bfaa 100644 --- a/apps/nestjs/src/entities/state.entity.ts +++ b/apps/nestjs/src/entities/state.entity.ts @@ -1,5 +1,5 @@ import { Field, ID, ObjectType } from '@nestjs/graphql' -import { Collection, Entity, IdentifiedReference, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' +import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' // import { BaseModel } from './base.model' // import { City } from './city.entity' import { UserSetting } from './user-setting.entity' @@ -49,9 +49,9 @@ export class State { @Field(() => String, { nullable: false }) longitude!: string - @ManyToOne({ entity: () => Country, nullable: false }) - @Field(() => [Country]) - country!: IdentifiedReference + @ManyToOne({ entity: () => Country }) + @Field(() => Country) + country!: Country @OneToMany(() => UserSetting, userSetting => userSetting.state, { eager: true, diff --git a/apps/nestjs/src/entities/subject.entity.ts b/apps/nestjs/src/entities/subject.entity.ts new file mode 100644 index 00000000..74ecbbbf --- /dev/null +++ b/apps/nestjs/src/entities/subject.entity.ts @@ -0,0 +1,13 @@ +import { Entity, Property, Unique } from '@mikro-orm/core' + +import { Field, ObjectType } from '@nestjs/graphql' +import { BaseModel } from './base.model' + +@Entity({ tableName: 'subject' }) +@Unique({ properties: ['name'] }) +@ObjectType() +export class Subject extends BaseModel { + @Property({ type: 'varchar', length: 200, nullable: false }) + @Field(() => String, { nullable: false }) + name!: string +} diff --git a/apps/nestjs/src/entities/user-role.entity.ts b/apps/nestjs/src/entities/user-role.entity.ts deleted file mode 100644 index bab8d2e4..00000000 --- a/apps/nestjs/src/entities/user-role.entity.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Cascade, Entity, ManyToOne, Unique } from '@mikro-orm/core' -import { Field, ObjectType } from '@nestjs/graphql' -import { BaseModel } from './base.model' -import { Role } from './role.entity' -import { User } from './user.entity' - -@Entity({ tableName: 'userRole' }) -@Unique({ properties: ['role', 'user'] }) -@ObjectType() -export class UserRole extends BaseModel { - @ManyToOne(() => Role, { - eager: false, - nullable: false, - cascade: [Cascade.REMOVE], - }) - @Field(() => Role, { nullable: false }) - role?: Role - - @ManyToOne(() => User, { - eager: false, - nullable: false, - onDelete: 'CASCADE', - }) - @Field(() => User, { nullable: false }) - user?: User -} diff --git a/apps/nestjs/src/entities/user.entity.ts b/apps/nestjs/src/entities/user.entity.ts index 1274c787..8bff211c 100644 --- a/apps/nestjs/src/entities/user.entity.ts +++ b/apps/nestjs/src/entities/user.entity.ts @@ -1,25 +1,13 @@ import { Field, HideField, ObjectType } from '@nestjs/graphql' -import { Cascade, Collection, Entity, OneToMany, OneToOne, Property, Unique } from '@mikro-orm/core' +import { Cascade, Collection, Entity, ManyToMany, OneToMany, OneToOne, Property, Unique } from '@mikro-orm/core' import { DateTimeResolver } from 'graphql-scalars' import { BaseModel } from './base.model' -// import { UserRole } from './user-role.entity' import { UserSetting } from './user-setting.entity' import { UserDeviceEntity } from './user-device.entity' import { NotificationToken } from './notification-token.entity' +import { Role } from './role.entity' import { TimestampType } from '@/core/transformer' -import { EncryptedType } from '@/core/utils/encrypted-type' - -// const transformer: Record<'date' | 'bigint', ValueTransformer> = { -// date: { -// from: (date: string | null) => date && new Date(parseInt(date, 10)), -// to: (date?: Date) => date?.valueOf().toString(), -// }, -// bigint: { -// from: (bigInt: string | null) => bigInt && parseInt(bigInt, 10), -// to: (bigInt?: number) => bigInt?.toString(), -// }, -// } @Entity({ tableName: 'user' }) @ObjectType() @@ -32,7 +20,7 @@ export class User extends BaseModel { @Field(() => String, { description: 'Example field (username)' }) username!: string - @Property({ type: EncryptedType, nullable: false }) + @Property({ type: 'varchar', nullable: false }) @Field(() => String, { description: 'Example field (name)' }) name!: string @@ -63,14 +51,13 @@ export class User extends BaseModel { @Property({ type: 'bool', nullable: false, default: false }) @Field(() => String, { description: 'Example field (placeholder)' }) - is_terms_accepted!: boolean + isTermsAccepted!: boolean @Field(() => DateTimeResolver, { description: 'last login time', }) @Property({ type: TimestampType, - name: 'last_login_at', comment: 'last login time', default: null, }) @@ -80,7 +67,6 @@ export class User extends BaseModel { description: 'Last login IP', }) @Property({ - name: 'last_login_ip', comment: 'Last login IP', default: null, }) @@ -104,12 +90,9 @@ export class User extends BaseModel { @OneToMany({ entity: () => NotificationToken, mappedBy: (token: NotificationToken) => token.createdBy, cascade: [Cascade.ALL], orphanRemoval: true }) notificationToken = new Collection(this) - // @OneToMany(() => UserRole, p => p.role, { - // eager: true, - // nullable: false, - // }) - // @Field(() => [UserRole], { nullable: false }) - // userRoles!: UserRole[] + @ManyToMany(() => Role, 'users', { owner: true }) + @Field(() => [Role], { nullable: true }) + roles = new Collection(this) @Field(() => UserSetting, { nullable: false }) @OneToOne(() => UserSetting, user => user.user, { diff --git a/apps/nestjs/src/modules/auth/auth.module.ts b/apps/nestjs/src/modules/auth/auth.module.ts index c6a64c38..8158ba3e 100644 --- a/apps/nestjs/src/modules/auth/auth.module.ts +++ b/apps/nestjs/src/modules/auth/auth.module.ts @@ -44,6 +44,6 @@ import { UserSetting } from '@/entities/user-setting.entity' SharedModule, ], providers: [AuthResolver, AuthService, JwtStrategy, AuthRepository, UserDeviceRepository], - exports: [AuthService], + exports: [AuthService, JwtStrategy], }) export class AuthModule {} diff --git a/apps/nestjs/src/modules/auth/auth.service.ts b/apps/nestjs/src/modules/auth/auth.service.ts index c8c14810..3b6f1e74 100644 --- a/apps/nestjs/src/modules/auth/auth.service.ts +++ b/apps/nestjs/src/modules/auth/auth.service.ts @@ -360,11 +360,13 @@ export class AuthService { const userAgent = UAParser(req.header('user-agent')) const jti = this.generateJti(userId) - + const user = await this.authRepository.findUserById(userId) + // TODO @uzunertoprak: roles donus yolu duzeltilecek const _accessToken = await this.generateAccessToken({ id: userId, username, jti, + roles: user.roles, }) const sessionData = new Session() diff --git a/apps/nestjs/src/modules/auth/repositories/auth.repo.ts b/apps/nestjs/src/modules/auth/repositories/auth.repo.ts index 8acf0d62..c87ef8e9 100644 --- a/apps/nestjs/src/modules/auth/repositories/auth.repo.ts +++ b/apps/nestjs/src/modules/auth/repositories/auth.repo.ts @@ -32,7 +32,7 @@ export class AuthRepository { ...data, lastLoginAt: new Date(), createdAt: new Date(), - is_terms_accepted: data.isTermsAccepted, + isTermsAccepted: data.isTermsAccepted, }) const newSettings = this.userSettingRepo.create({ @@ -70,4 +70,18 @@ export class AuthRepository { throw new HttpException(await this.i18n.error('ierror.user_dont_found'), HttpStatus.UNPROCESSABLE_ENTITY) return user } + + // simdi burada hangi tabloya baglanmamiz gerekiyor + public async findUserById(id: string): Promise { + const user = await this.userRepository.createQueryBuilder('user') + .leftJoinAndSelect('user.roles', 'userRole') + .leftJoinAndSelect('userRole.permissions', 'userRolePermission') + .leftJoinAndSelect('userRolePermission.subject', 'userRolePermissionSubject') + .where(`"user".id = '${id}'`) + .getSingleResult() + + if (!user) + throw new HttpException(await this.i18n.error('ierror.user_dont_found'), HttpStatus.UNPROCESSABLE_ENTITY) + return user + } } diff --git a/apps/nestjs/src/modules/auth/types/jwt.args.ts b/apps/nestjs/src/modules/auth/types/jwt.args.ts index 8b60c472..af6ce5a9 100644 --- a/apps/nestjs/src/modules/auth/types/jwt.args.ts +++ b/apps/nestjs/src/modules/auth/types/jwt.args.ts @@ -1,6 +1,8 @@ +import { Permission, Role } from '@/entities' + export interface JwtArgs { id: string - roles?: string[] + roles?: Role[] | unknown /** * Issued at */ @@ -12,7 +14,7 @@ export interface JwtArgs { token_type?: 'access' | 'refresh' jti: string username: string - permissions?: string[] + permissions?: Permission[] aud?: string iss?: string diff --git a/apps/nestjs/src/modules/casl/README.md b/apps/nestjs/src/modules/casl/README.md new file mode 100644 index 00000000..0cfd86c6 --- /dev/null +++ b/apps/nestjs/src/modules/casl/README.md @@ -0,0 +1,117 @@ +# Role Based Access Control + +## Usage + +### Create Role +
+ +
  • First you should create a role like `Admin`, `Moderator`, `Guest`
  • + +
  • Go createPermission in graphql and create one or more.
  • +
    + +### Create Subject + +
  • Now you should declare a subject, subject means collection or module like `BLOG`, `TODO`, `POST`
  • + +
  • Its only a string just like role, we will use it in resolver level soon. Go createSubject in graphql and create one or more.
  • +
    + +### Create Permission + +
  • Select a subject which is you want to create permission on it
  • + +
  • Go createPermission in graphql and create permissions for crud with giving subject id
  • + +
    + +```json +{ + "permission": { + "action": "CREATE", + "subject": { + "name": "BLOG" + } + } +} +``` + +### Add Permissions To Role + +
  • Go addPermissionToRole in graphql then add with permission id and role id
  • +
    + +```json +{ + "role": { + "name": "Admin", + "permissions": [ + { + "action": "CREATE", + "subject": { + "name": "BLOG" + } + }, + { + "action": "READ", + "subject": { + "name": "BLOG" + } + }, + { + "action": "DELETE", + "subject": { + "name": "BLOG" + } + }, + { + "action": "UPDATE", + "subject": { + "name": "BLOG" + } + } + ] + } +} +``` +
    + +### Permission Guard and Checker +
    + +```typescript +@UseGuards(PermissionsGuard) +@CheckPermissions([ActionEnum.CREATE, "BLOG"]) +@Mutation(() => Blog) +async createAdminRole(@Args('data') data: CreateBlogInput): Promise { + return await this.blogService.createBlog(data) +} +``` + +
  • Now user need role like this for access the mutation
  • +
    + +```json +{ + "role": { + "name": "whatever", + "permissions": [ + { + "action": "CREATE", + "subject": { + "name": "BLOG" + } + } + ] + } + +} +``` +
    + +
  • BLOG is subject you created in database before, and action comes from permission with subject. Permission Guard will find all permissions from user's roles and CheckPermissions will check is user have this permission.
  • +
    + +```typescript +@CheckPermissions([ActionEnum.CREATE, "BLOG"]) +``` \ No newline at end of file diff --git a/apps/nestjs/src/modules/casl/casl-ability.factory.ts b/apps/nestjs/src/modules/casl/casl-ability.factory.ts new file mode 100644 index 00000000..056bfd8f --- /dev/null +++ b/apps/nestjs/src/modules/casl/casl-ability.factory.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common' +import { Ability } from '@casl/ability' +import { AppAbilityType } from '@/modules/casl/interfaces/app-ability.type' +import { PermissionInterface } from '@/modules/casl/interfaces/permission.interface' +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' +import { PermissionObjectType } from '@/modules/casl/interfaces/permission-object.type' + +@Injectable() +export class CaslAbilityFactory { + /** + * It takes an array of permissions and returns an ability object + * @param {Permission[] | undefined} permissions - Permission[] | undefined + * @returns An ability object + */ + async createForUser(permissions: any[] | undefined): Promise { + if (!permissions) + throw new Error(permissions) + + const caslPermissions: PermissionInterface[] = permissions.map(p => ( + { + action: p.action, + subject: p.subject, + })) + return new Ability<[ActionEnum, PermissionObjectType]>(caslPermissions) + } +} diff --git a/apps/nestjs/src/modules/casl/casl.module.ts b/apps/nestjs/src/modules/casl/casl.module.ts new file mode 100644 index 00000000..cb8c29ac --- /dev/null +++ b/apps/nestjs/src/modules/casl/casl.module.ts @@ -0,0 +1,46 @@ +import { Module } from '@nestjs/common' +import { MikroOrmModule } from '@mikro-orm/nestjs' +import { AuthModule } from '../auth/auth.module' +import { PermissionRepository, RoleRepository, SubjectRepository } from './repositories' +import { RoleService } from './services/role.service' +import { PermissionService } from './services/permission.service' +import { SubjectService } from './services/subject.service' +import { SubjectResolver } from './resolvers/subject.resolver' +import { CaslUserRepository } from './repositories/user.repo' +import { CalsRoleResolver } from './resolvers/role.resolver' +import { CalsPermissionResolver } from './resolvers/permission.resolver' +import { Permission, Role, Subject, User } from '@/entities' +import { PermissionsGuard } from '@/modules/casl/guards/permissions.guard' +import { CaslAbilityFactory } from '@/modules/casl/casl-ability.factory' + +const repositories = [ + RoleRepository, + PermissionRepository, + SubjectRepository, + CaslUserRepository, +] + +const services = [ + RoleService, + PermissionService, + SubjectService, +] + +const resolvers = [ + SubjectResolver, + CalsRoleResolver, + CalsPermissionResolver, +] + +@Module({ + imports: [MikroOrmModule.forFeature({ entities: [Role, Permission, Subject, User] }), AuthModule], + providers: [ + CaslAbilityFactory, + PermissionsGuard, + ...services, + ...repositories, + ...resolvers, + ], + exports: [CaslAbilityFactory, PermissionsGuard, RoleRepository, PermissionRepository, SubjectRepository], +}) +export class CaslModule { } diff --git a/apps/nestjs/src/modules/casl/decorators/check-permissions.decorator.ts b/apps/nestjs/src/modules/casl/decorators/check-permissions.decorator.ts new file mode 100644 index 00000000..5dc4d959 --- /dev/null +++ b/apps/nestjs/src/modules/casl/decorators/check-permissions.decorator.ts @@ -0,0 +1,15 @@ +import { CustomDecorator, SetMetadata } from '@nestjs/common' +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' +import { PermissionObjectType } from '@/modules/casl/interfaces/permission-object.type' + +export type RequiredPermission = [ActionEnum, PermissionObjectType] +export const PERMISSION_CHECKER_KEY = 'permission_checker_params_key' + +/** + * It takes a list of permissions and returns a decorator that sets the PERMISSION_CHECKER_KEY metadata + * on the decorated class or method + * @param {RequiredPermission[]} params - RequiredPermission[] - This is an array of RequiredPermission + * objects. + */ +export const CheckPermissions = (...params: RequiredPermission[]): CustomDecorator => + SetMetadata(PERMISSION_CHECKER_KEY, params) diff --git a/apps/nestjs/src/modules/casl/guards/permissions.guard.ts b/apps/nestjs/src/modules/casl/guards/permissions.guard.ts new file mode 100644 index 00000000..93abe79c --- /dev/null +++ b/apps/nestjs/src/modules/casl/guards/permissions.guard.ts @@ -0,0 +1,61 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { GqlExecutionContext } from '@nestjs/graphql' +import { AppAbilityType } from '@/modules/casl/interfaces/app-ability.type' +import { PERMISSION_CHECKER_KEY, RequiredPermission } from '@/modules/casl/decorators/check-permissions.decorator' +import { CaslAbilityFactory } from '@/modules/casl/casl-ability.factory' +import { RequiredPermissionType } from '@/modules/casl/interfaces/required-permission.type' + +@Injectable() +export class PermissionsGuard implements CanActivate { + constructor( + private reflector: Reflector, + private abilityFactory: CaslAbilityFactory, + ) {} + + /** + * It gets the required permissions from the decorator, creates an ability object for the user, and + * then checks if the user has the required permissions + * @param {ExecutionContext} context - ExecutionContext - This is the context of the request. + * @returns A boolean value. + */ + async canActivate(context: ExecutionContext): Promise { + const requiredPermissions: RequiredPermissionType[] + = this.reflector.get( + PERMISSION_CHECKER_KEY, + context.getHandler(), + ) || [] + + const roles = GqlExecutionContext.create(context).getContext().req.user.roles + + const permissions: any[] = [] + + roles.forEach((r: any) => { + r.permissions.forEach((p: any) => { + permissions.push({ action: p.action, subject: p.subject.name }) + }) + }) + + const ability = await this.abilityFactory.createForUser( + permissions, + ) + + return requiredPermissions.every(permission => + PermissionsGuard.isAllowed(ability, permission), + ) + } + + /** + * It returns true if the ability can do the permission + * @param {AppAbilityType} ability - AppAbilityType - This is the ability object that we created in + * the previous step. + * @param {RequiredPermissionType} permission - This is the permission that you want to check. + * @returns A boolean value + */ + private static isAllowed( + ability: AppAbilityType, + permission: RequiredPermissionType, + ): boolean { + return ability.can(...permission) + } +} diff --git a/apps/nestjs/src/modules/casl/inputs/add-permission.input.ts b/apps/nestjs/src/modules/casl/inputs/add-permission.input.ts new file mode 100644 index 00000000..1f9f3ffc --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/add-permission.input.ts @@ -0,0 +1,11 @@ +import { Field, InputType } from '@nestjs/graphql' + +@InputType() +// bu input hem remove için hem add için kullanılıyor daha uygun bi isim gerekiyor +export class AddPermissionInput { + @Field() + permissionId!: string + + @Field() + roleId!: string +} diff --git a/apps/nestjs/src/modules/casl/inputs/add-role.input.ts b/apps/nestjs/src/modules/casl/inputs/add-role.input.ts new file mode 100644 index 00000000..a2665301 --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/add-role.input.ts @@ -0,0 +1,11 @@ +import { Field, InputType } from '@nestjs/graphql' + +@InputType() +// bu input hem remove için hem add için kullanılıyor daha uygun bi isim gerekiyor +export class AddRoleInput { + @Field() + userId!: string + + @Field() + roleId!: string +} diff --git a/apps/nestjs/src/modules/casl/inputs/permission/create-permission.input.ts b/apps/nestjs/src/modules/casl/inputs/permission/create-permission.input.ts new file mode 100644 index 00000000..1b1f94d9 --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/permission/create-permission.input.ts @@ -0,0 +1,12 @@ +import { Field, ID, InputType } from '@nestjs/graphql' +import { ActionEnum } from '../../interfaces/action.enum' + +@InputType() +export class CreatePermissionInput { + @Field(() => ActionEnum) + action!: ActionEnum + + @Field(() => ID) + subjectId!: string +} + diff --git a/apps/nestjs/src/modules/casl/inputs/permission/update-permission.input.ts b/apps/nestjs/src/modules/casl/inputs/permission/update-permission.input.ts new file mode 100644 index 00000000..ec16dead --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/permission/update-permission.input.ts @@ -0,0 +1,5 @@ +import { InputType, PartialType } from '@nestjs/graphql' +import { CreatePermissionInput } from './create-permission.input' + +@InputType() +export class UpdatePermissionInput extends PartialType(CreatePermissionInput) {} diff --git a/apps/nestjs/src/modules/casl/inputs/role/create-role.input.ts b/apps/nestjs/src/modules/casl/inputs/role/create-role.input.ts new file mode 100644 index 00000000..c24a5174 --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/role/create-role.input.ts @@ -0,0 +1,14 @@ +import { Field, InputType } from '@nestjs/graphql' +import { IsString } from 'class-validator' + +@InputType() +export class CreateAdminRoleInput { + @IsString() + @Field() + name!: string + + @IsString() + @Field() + description!: string +} + diff --git a/apps/nestjs/src/modules/casl/inputs/role/update-role.input.ts b/apps/nestjs/src/modules/casl/inputs/role/update-role.input.ts new file mode 100644 index 00000000..424fb5f4 --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/role/update-role.input.ts @@ -0,0 +1,5 @@ +import { InputType, PartialType } from '@nestjs/graphql' +import { CreateAdminRoleInput } from './create-role.input' + +@InputType() +export class UpdateRoleInput extends PartialType(CreateAdminRoleInput) {} diff --git a/apps/nestjs/src/modules/casl/inputs/subject/create-subject.input.ts b/apps/nestjs/src/modules/casl/inputs/subject/create-subject.input.ts new file mode 100644 index 00000000..2757b665 --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/subject/create-subject.input.ts @@ -0,0 +1,7 @@ +import { Field, InputType } from '@nestjs/graphql' + +@InputType() +export class CreateSubjectInput { + @Field() + name!: string +} diff --git a/apps/nestjs/src/modules/casl/inputs/subject/update-subject.input.ts b/apps/nestjs/src/modules/casl/inputs/subject/update-subject.input.ts new file mode 100644 index 00000000..1e19ecff --- /dev/null +++ b/apps/nestjs/src/modules/casl/inputs/subject/update-subject.input.ts @@ -0,0 +1,5 @@ +import { InputType, PartialType } from '@nestjs/graphql' +import { CreateSubjectInput } from './create-subject.input' + +@InputType() +export class UpdateSubjectInput extends PartialType(CreateSubjectInput) {} diff --git a/apps/nestjs/src/modules/casl/interfaces/action.enum.ts b/apps/nestjs/src/modules/casl/interfaces/action.enum.ts new file mode 100644 index 00000000..1a03a883 --- /dev/null +++ b/apps/nestjs/src/modules/casl/interfaces/action.enum.ts @@ -0,0 +1,13 @@ +import { registerEnumType } from '@nestjs/graphql' + +export enum ActionEnum { + MANAGE = 'manage', + CREATE = 'create', + READ = 'read', + UPDATE = 'update', + DELETE = 'delete', +} + +registerEnumType(ActionEnum, { + name: 'ActionEnum', +}) diff --git a/apps/nestjs/src/modules/casl/interfaces/app-ability.type.ts b/apps/nestjs/src/modules/casl/interfaces/app-ability.type.ts new file mode 100644 index 00000000..9080f986 --- /dev/null +++ b/apps/nestjs/src/modules/casl/interfaces/app-ability.type.ts @@ -0,0 +1,5 @@ +import { Ability } from '@casl/ability' +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' +import { PermissionObjectType } from '@/modules/casl/interfaces/permission-object.type' + +export type AppAbilityType = Ability<[ActionEnum, PermissionObjectType]> diff --git a/apps/nestjs/src/modules/casl/interfaces/permission-object.type.ts b/apps/nestjs/src/modules/casl/interfaces/permission-object.type.ts new file mode 100644 index 00000000..0f5be82d --- /dev/null +++ b/apps/nestjs/src/modules/casl/interfaces/permission-object.type.ts @@ -0,0 +1 @@ +export type PermissionObjectType = any diff --git a/apps/nestjs/src/modules/casl/interfaces/permission.interface.ts b/apps/nestjs/src/modules/casl/interfaces/permission.interface.ts new file mode 100644 index 00000000..f0be6efa --- /dev/null +++ b/apps/nestjs/src/modules/casl/interfaces/permission.interface.ts @@ -0,0 +1,6 @@ +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' + +export interface PermissionInterface { + action: ActionEnum + subject: string +} diff --git a/apps/nestjs/src/modules/casl/interfaces/required-permission.type.ts b/apps/nestjs/src/modules/casl/interfaces/required-permission.type.ts new file mode 100644 index 00000000..d836946f --- /dev/null +++ b/apps/nestjs/src/modules/casl/interfaces/required-permission.type.ts @@ -0,0 +1,7 @@ +import { ActionEnum } from '@/modules/casl/interfaces/action.enum' +import { PermissionObjectType } from '@/modules/casl/interfaces/permission-object.type' + +export type RequiredPermissionType = [ + ActionEnum, + PermissionObjectType, +] diff --git a/apps/nestjs/src/modules/casl/repositories/index.ts b/apps/nestjs/src/modules/casl/repositories/index.ts new file mode 100644 index 00000000..b8596bea --- /dev/null +++ b/apps/nestjs/src/modules/casl/repositories/index.ts @@ -0,0 +1,4 @@ +export { RoleRepository } from './role.repo' +export { PermissionRepository } from './permission.repo' +export { SubjectRepository } from './subject.repo' +export { CaslUserRepository } from './user.repo' diff --git a/apps/nestjs/src/modules/casl/repositories/permission.repo.ts b/apps/nestjs/src/modules/casl/repositories/permission.repo.ts new file mode 100644 index 00000000..141ba659 --- /dev/null +++ b/apps/nestjs/src/modules/casl/repositories/permission.repo.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@mikro-orm/nestjs' +import { EntityRepository } from '@mikro-orm/postgresql' +import { CreatePermissionInput } from '../inputs/permission/create-permission.input' +import { UpdatePermissionInput } from '../inputs/permission/update-permission.input' +import { SubjectRepository } from './subject.repo' +import { Permission } from '@/entities' + +@Injectable() +export class PermissionRepository { + constructor( + @InjectRepository(Permission) + private readonly permissionRepo: EntityRepository, + private readonly subjectRepo: SubjectRepository, + ) { } + + async createPermission(data: CreatePermissionInput): Promise { + const subject = await this.subjectRepo.getSubjectById(data.subjectId) + const newPermission = this.permissionRepo.create({ subject: subject.toObject(), action: data.action }) + await this.permissionRepo.persistAndFlush(newPermission) + return newPermission + } + + async getAllPermissions(): Promise { + return await this.permissionRepo.findAll() + } + + async getPermissionById(id: string): Promise { + return await this.permissionRepo.findOneOrFail({ id }) + } + + async deletePermission(id: number): Promise { + await this.permissionRepo.removeAndFlush({ id }) + } + + async updatePermission(id: string, updatePermissionInput: UpdatePermissionInput): Promise { + return await this.permissionRepo.createQueryBuilder().update(updatePermissionInput).where({ id }).execute() + } +} diff --git a/apps/nestjs/src/modules/casl/repositories/role.repo.ts b/apps/nestjs/src/modules/casl/repositories/role.repo.ts new file mode 100644 index 00000000..4edda17a --- /dev/null +++ b/apps/nestjs/src/modules/casl/repositories/role.repo.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@mikro-orm/nestjs' +import { EntityRepository } from '@mikro-orm/postgresql' +import { CreateAdminRoleInput } from '../inputs/role/create-role.input' +import { UpdateRoleInput } from '../inputs/role/update-role.input' +import { AddPermissionInput } from '../inputs/add-permission.input' +import { PermissionRepository } from './permission.repo' +import { Role } from '@/entities' + +@Injectable() +export class RoleRepository { + constructor( + @InjectRepository(Role) + private readonly roleRepo: EntityRepository, + private readonly permissionRepo: PermissionRepository, + ) { } + + async createAdminRole(createRoleInput: CreateAdminRoleInput): Promise { + const newRole = this.roleRepo.create(createRoleInput) + await this.roleRepo.persistAndFlush(newRole) + return newRole + } + + async addPermissionToRole(data: AddPermissionInput): Promise { + const permission = await this.permissionRepo.getPermissionById(data.permissionId) + const role = await this.roleRepo.findOneOrFail({ id: data.roleId }, { populate: ['permissions'] }) + role.permissions.add(permission) + await this.roleRepo.persistAndFlush(role) + return role + } + + async removePermissionFromRole(data: AddPermissionInput): Promise { + const permission = await this.permissionRepo.getPermissionById(data.permissionId) + const role = await this.roleRepo.findOneOrFail({ id: data.roleId }, { populate: ['permissions'] }) + role.permissions.remove(permission) + await this.roleRepo.persistAndFlush(role) + return role + } + + async getAllRoles(): Promise { + return await this.roleRepo.findAll({ populate: ['permissions'] }) + } + + async getRoleByName(name: string): Promise { + return await this.roleRepo.findOneOrFail({ name }) + } + + async getRoleById(id: string): Promise { + return await this.roleRepo.findOneOrFail({ id }) + } + + async deleteRole(id: string): Promise { + await this.roleRepo.removeAndFlush({ id }) + } + + async updateRole(id: string, updateRoleInput: UpdateRoleInput): Promise { + return await this.roleRepo.createQueryBuilder().update(updateRoleInput).where({ id }).execute() + } +} diff --git a/apps/nestjs/src/modules/casl/repositories/subject.repo.ts b/apps/nestjs/src/modules/casl/repositories/subject.repo.ts new file mode 100644 index 00000000..a1dc2bcf --- /dev/null +++ b/apps/nestjs/src/modules/casl/repositories/subject.repo.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@nestjs/common' +import { InjectRepository } from '@mikro-orm/nestjs' +import { EntityRepository } from '@mikro-orm/postgresql' +import { CreateSubjectInput } from '../inputs/subject/create-subject.input' +import { UpdateSubjectInput } from '../inputs/subject/update-subject.input' +import { Subject } from '@/entities' + +@Injectable() +export class SubjectRepository { + constructor( + @InjectRepository(Subject) + private readonly subjectRepo: EntityRepository, + ) { } + + async createSubject(createSubjectInput: CreateSubjectInput): Promise { + const newSubject = this.subjectRepo.create(createSubjectInput) + await this.subjectRepo.persistAndFlush(newSubject) + return newSubject + } + + async getAllSubjects(): Promise { + return await this.subjectRepo.findAll() + } + + async getSubjectByName(name: string): Promise { + return await this.subjectRepo.findOneOrFail({ name }) + } + + async getSubjectById(id: string): Promise { + return await this.subjectRepo.findOneOrFail({ id }) + } + + async deleteSubject(id: string): Promise { + await this.subjectRepo.removeAndFlush({ id }) + } + + async updateSubject(id: string, updateSubjectInput: UpdateSubjectInput): Promise { + return await this.subjectRepo.createQueryBuilder().update(updateSubjectInput).where({ id }).execute() + } +} diff --git a/apps/nestjs/src/modules/casl/repositories/user.repo.ts b/apps/nestjs/src/modules/casl/repositories/user.repo.ts new file mode 100644 index 00000000..2d40f2d7 --- /dev/null +++ b/apps/nestjs/src/modules/casl/repositories/user.repo.ts @@ -0,0 +1,35 @@ +import { + Injectable, +} from '@nestjs/common' +import { InjectRepository } from '@mikro-orm/nestjs' +import { EntityRepository } from '@mikro-orm/postgresql' +import { AddRoleInput } from '../inputs/add-role.input' +import { User } from '@/entities' +import { RoleRepository } from '@/modules/casl/repositories' + +@Injectable() +export class CaslUserRepository { + constructor( + @InjectRepository(User) + private readonly userRepo: EntityRepository, + private readonly roleRepo: RoleRepository, + ) { + } + + async addRoleToUser(addRoleInput: AddRoleInput): Promise { + const role = await this.roleRepo.getRoleById(addRoleInput.roleId) + const user = await this.userRepo.findOneOrFail({ id: addRoleInput.userId }, { populate: ['roles'] }) + user.roles.add(role) + await this.userRepo.persistAndFlush(role) + return user + } + + async removeRoleFromUser(addRoleInput: AddRoleInput): Promise { + const role = await this.roleRepo.getRoleById(addRoleInput.roleId) + const user = await this.userRepo.findOneOrFail({ id: addRoleInput.userId }, { populate: ['roles'] }) + user.roles.remove(role) + await this.userRepo.persistAndFlush(user) + return user + } +} + diff --git a/apps/nestjs/src/modules/casl/resolvers/permission.resolver.ts b/apps/nestjs/src/modules/casl/resolvers/permission.resolver.ts new file mode 100644 index 00000000..e65ecf6c --- /dev/null +++ b/apps/nestjs/src/modules/casl/resolvers/permission.resolver.ts @@ -0,0 +1,31 @@ +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' +import { CreatePermissionInput } from '../inputs/permission/create-permission.input' +import { PermissionService } from '../services/permission.service' +import { UpdatePermissionInput } from '../inputs/permission/update-permission.input' +import { Permission } from '@/entities' + +@Resolver() +export class CalsPermissionResolver { + constructor(private readonly permissionService: PermissionService) {} + + @Query(() => [Permission]) + async permissions(): Promise { + return await this.permissionService.getAllPermissions() + } + + @Mutation(() => Permission) + async createPermission(@Args('data') data: CreatePermissionInput): Promise { + return await this.permissionService.createPermission(data) + } + + @Mutation(() => Permission) + async updatePermission(@Args('id') id: string, @Args('data') data: UpdatePermissionInput): Promise { + return await this.permissionService.updatePermission(id, data) + } + + @Mutation(() => Permission) + async deletePermission(@Args('id') id: string): Promise { + await this.permissionService.deletePermission(id) + return true + } +} diff --git a/apps/nestjs/src/modules/casl/resolvers/role.resolver.ts b/apps/nestjs/src/modules/casl/resolvers/role.resolver.ts new file mode 100644 index 00000000..2c2b26e3 --- /dev/null +++ b/apps/nestjs/src/modules/casl/resolvers/role.resolver.ts @@ -0,0 +1,58 @@ +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' +import { CreateAdminRoleInput } from '../inputs/role/create-role.input' +import { RoleService } from '../services/role.service' +import { UpdateRoleInput } from '../inputs/role/update-role.input' +import { AddPermissionInput } from '../inputs/add-permission.input' +import { AddRoleInput } from '../inputs/add-role.input' +import { Role, User } from '@/entities' + +@Resolver() +export class CalsRoleResolver { + constructor(private readonly roleService: RoleService) {} + + @Mutation(() => Role) + async createAdminRole(@Args('data') data: CreateAdminRoleInput): Promise { + return await this.roleService.createAdminRole(data) + } + + @Mutation(() => Role) + async addPermissionToRole(@Args('addPermission') data: AddPermissionInput): Promise { + return await this.roleService.addPermissionToRole(data) + } + + @Mutation(() => Role) + async removePermissionFromRole(@Args('removePermission') data: AddPermissionInput): Promise { + return await this.roleService.removePermissionFromRole(data) + } + + @Mutation(() => Role) + async addRoleToUser(@Args('addRole') data: AddRoleInput): Promise { + return await this.roleService.addRoleToUser(data) + } + + @Mutation(() => Role) + async removeRoleFromUser(@Args('addRole') data: AddRoleInput): Promise { + return await this.roleService.removeRoleFromUser(data) + } + + @Query(() => [Role]) + async roles(): Promise { + return await this.roleService.getAllRoles() + } + + @Query(() => Role) + async role(@Args('name') name: string): Promise { + return await this.roleService.getRoleByName(name) + } + + @Mutation(() => Role) + async updateRole(@Args('id') id: string, @Args('data') data: UpdateRoleInput): Promise { + return await this.roleService.updateRole(id, data) + } + + @Mutation(() => Role) + async deleteRole(@Args('id') id: string): Promise { + await this.roleService.deleteRole(id) + return true + } +} diff --git a/apps/nestjs/src/modules/casl/resolvers/subject.resolver.ts b/apps/nestjs/src/modules/casl/resolvers/subject.resolver.ts new file mode 100644 index 00000000..6b9aaaf4 --- /dev/null +++ b/apps/nestjs/src/modules/casl/resolvers/subject.resolver.ts @@ -0,0 +1,36 @@ +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql' +import { CreateSubjectInput } from '../inputs/subject/create-subject.input' +import { UpdateSubjectInput } from '../inputs/subject/update-subject.input' +import { SubjectService } from '../services/subject.service' +import { Subject } from '@/entities' + +@Resolver() +export class SubjectResolver { + constructor(private readonly subjectService: SubjectService) {} + + @Query(() => [Subject]) + async subjects(): Promise { + return await this.subjectService.getAllSubjects() + } + + @Query(() => Subject) + async subject(@Args('name') name: string): Promise { + return await this.subjectService.getSubjectByName(name) + } + + @Mutation(() => Subject) + async createSubject(@Args('data') data: CreateSubjectInput): Promise { + return await this.subjectService.createSubject(data) + } + + @Mutation(() => Subject) + async updateSubject(@Args('id') id: string, @Args('data') data: UpdateSubjectInput): Promise { + return await this.subjectService.updateSubject(id, data) + } + + @Mutation(() => Subject) + async deleteSubject(@Args('id') id: string): Promise { + await this.subjectService.deleteSubject(id) + return true + } +} diff --git a/apps/nestjs/src/modules/casl/services/permission.service.ts b/apps/nestjs/src/modules/casl/services/permission.service.ts new file mode 100644 index 00000000..919567c4 --- /dev/null +++ b/apps/nestjs/src/modules/casl/services/permission.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@nestjs/common' +import { CreatePermissionInput } from '../inputs/permission/create-permission.input' +import { UpdatePermissionInput } from '../inputs/permission/update-permission.input' +import { PermissionRepository } from '../repositories' +import { Permission } from '@/entities' + +@Injectable() +export class PermissionService { + constructor(private readonly permissionRepository: PermissionRepository) {} + + async createPermission(data: CreatePermissionInput): Promise { + return await this.permissionRepository.createPermission(data) + } + + async getAllPermissions(): Promise { + return await this.permissionRepository.getAllPermissions() + } + + async getPermissionById(id: string): Promise { + return await this.permissionRepository.getPermissionById(id) + } + + async deletePermission(id: string): Promise { + await this.deletePermission(id) + return 'deleted' + } + + async updatePermission(id: string, updatePermissionInput: UpdatePermissionInput): Promise { + return await this.updatePermission(id, updatePermissionInput) + } +} diff --git a/apps/nestjs/src/modules/casl/services/role.service.ts b/apps/nestjs/src/modules/casl/services/role.service.ts new file mode 100644 index 00000000..864b9bbd --- /dev/null +++ b/apps/nestjs/src/modules/casl/services/role.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@nestjs/common' +import { CreateAdminRoleInput } from '../inputs/role/create-role.input' +import { UpdateRoleInput } from '../inputs/role/update-role.input' +import { CaslUserRepository, RoleRepository } from '../repositories' +import { AddPermissionInput } from '../inputs/add-permission.input' +import { AddRoleInput } from '../inputs/add-role.input' +import { Role, User } from '@/entities' + +@Injectable() +export class RoleService { + constructor(private readonly roleRepository: RoleRepository, + private readonly userRepo: CaslUserRepository) {} + + async createAdminRole(createRoleInput: CreateAdminRoleInput): Promise { + return await this.roleRepository.createAdminRole(createRoleInput) + } + + async addPermissionToRole(data: AddPermissionInput): Promise { + return await this.roleRepository.addPermissionToRole(data) + } + + async removePermissionFromRole(data: AddPermissionInput): Promise { + return await this.roleRepository.removePermissionFromRole(data) + } + + async addRoleToUser(data: AddRoleInput): Promise { + return await this.userRepo.addRoleToUser(data) + } + + async removeRoleFromUser(data: AddRoleInput): Promise { + return await this.userRepo.removeRoleFromUser(data) + } + + async getAllRoles(): Promise { + return await this.roleRepository.getAllRoles() + } + + async getRoleByName(name: string): Promise { + return await this.roleRepository.getRoleByName(name) + } + + async getRoleById(id: string): Promise { + return await this.roleRepository.getRoleById(id) + } + + async deleteRole(id: string): Promise { + await this.deleteRole(id) + return 'deleted' + } + + async updateRole(id: string, updateRoleInput: UpdateRoleInput): Promise { + return await this.updateRole(id, updateRoleInput) + } +} diff --git a/apps/nestjs/src/modules/casl/services/subject.service.ts b/apps/nestjs/src/modules/casl/services/subject.service.ts new file mode 100644 index 00000000..f1e06655 --- /dev/null +++ b/apps/nestjs/src/modules/casl/services/subject.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common' +import { CreateSubjectInput } from '../inputs/subject/create-subject.input' +import { UpdateSubjectInput } from '../inputs/subject/update-subject.input' +import { SubjectRepository } from '../repositories' +import { Subject } from '@/entities' + +@Injectable() +export class SubjectService { + constructor(private readonly subjectRepository: SubjectRepository) {} + + async createSubject(createSubjectInput: CreateSubjectInput): Promise { + return await this.subjectRepository.createSubject(createSubjectInput) + } + + async getAllSubjects(): Promise { + return await this.subjectRepository.getAllSubjects() + } + + async getSubjectByName(name: string): Promise { + return await this.subjectRepository.getSubjectByName(name) + } + + async getSubjectById(id: string): Promise { + return await this.subjectRepository.getSubjectById(id) + } + + async deleteSubject(id: string): Promise { + await this.deleteSubject(id) + return 'deleted' + } + + async updateSubject(id: string, updateSubjectInput: UpdateSubjectInput): Promise { + return await this.updateSubject(id, updateSubjectInput) + } +} diff --git a/apps/nestjs/src/modules/user/inputs/create-user.input.ts b/apps/nestjs/src/modules/user/inputs/create-user.input.ts new file mode 100644 index 00000000..9a108634 --- /dev/null +++ b/apps/nestjs/src/modules/user/inputs/create-user.input.ts @@ -0,0 +1,7 @@ +import { Field, InputType } from '@nestjs/graphql' + +@InputType('UserInput') +export class CreateUserInput { + @Field() + name!: string +} diff --git a/apps/nestjs/src/modules/user/repositories/user.repo.ts b/apps/nestjs/src/modules/user/repositories/user.repo.ts index 559e6224..c37be5b2 100644 --- a/apps/nestjs/src/modules/user/repositories/user.repo.ts +++ b/apps/nestjs/src/modules/user/repositories/user.repo.ts @@ -4,19 +4,19 @@ import { import { InjectRepository } from '@mikro-orm/nestjs' import { EntityRepository } from '@mikro-orm/postgresql' import { User } from '@/entities' +import { RoleRepository } from '@/modules/casl/repositories' @Injectable() export class UserRepository { constructor( @InjectRepository(User) private readonly userRepo: EntityRepository, + private readonly roleRepo: RoleRepository, ) { } - public async findOne(username: string) { - const user = await this.userRepo.findOne({ - id: username, - }) + public async findOne(id: string) { + const user = await this.userRepo.findOneOrFail({ id }, { populate: ['roles.permissions'] }) return user } } diff --git a/apps/nestjs/src/modules/user/user.module.ts b/apps/nestjs/src/modules/user/user.module.ts index 9658f691..3760306c 100644 --- a/apps/nestjs/src/modules/user/user.module.ts +++ b/apps/nestjs/src/modules/user/user.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common' import { MikroOrmModule } from '@mikro-orm/nestjs' +import { CaslModule } from '../casl/casl.module' import { UserService } from './user.service' import { UserResolver } from './user.resolver' import { UserRepository } from './repositories/user.repo' @@ -12,6 +13,7 @@ import { NotificationService } from '@/shared/services/notification.service' imports: [ MikroOrmModule.forFeature({ entities: [User, NotificationToken] }), SharedModule, + CaslModule, ], providers: [UserResolver, UserService, UserRepository, NotificationTokenRepository, NotificationService], exports: [UserService, UserRepository], diff --git a/apps/nestjs/src/modules/user/user.service.ts b/apps/nestjs/src/modules/user/user.service.ts index d8f9968a..4a72fd8d 100644 --- a/apps/nestjs/src/modules/user/user.service.ts +++ b/apps/nestjs/src/modules/user/user.service.ts @@ -13,8 +13,8 @@ export class UserService { private readonly notiService: NotificationService, ) {} - async me(username: string) { - return this.userRepo.findOne(username) + async me(id: string) { + return this.userRepo.findOne(id) } async createToken(data: CreateNotificationTokenInput, user: JwtArgs) { diff --git a/package.json b/package.json index 97ea1377..83ee2999 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,13 @@ "mail:build": "pnpm --filter mail build", "ionic:build": "pnpm --filter nestjs build", "api:dev": "pnpm --filter nestjs dev", + "db:init": "pnpm --filter app-nestjs migrate:create", "db:create": "pnpm --filter app-nestjs migrate:create", "db:update": "pnpm --filter app-nestjs migrate:update", - "db:push": "pnpm --filter app-nestjs migrate:push", - "db:delete": "pnpm --filter app-nestjs migrate:drop", - "db:data": "pnpm --filter nestjs mikro-orm seeder:run", - "db:revert": "pnpm --filter app-nestjs migrate:revert", + "db:up": "pnpm --filter app-nestjs migrate:up", + "db:down": "pnpm --filter app-nestjs migrate:down", + "db:seed": "pnpm --filter app-nestjs seeder:run", + "db:fresh": "pnpm --filter app-nestjs migrate:fresh", "db:show": "pnpm --filter app-nestjs migrate:show", "generate": "pnpm --filter @oku/api build && pnpm --filter api app:create", "api:build": "pnpm --filter @oku/api graphql-ts", diff --git a/packages/oku-api/node-graphql.ts b/packages/oku-api/node-graphql.ts index ce2502c0..b572f2d0 100644 --- a/packages/oku-api/node-graphql.ts +++ b/packages/oku-api/node-graphql.ts @@ -258,14 +258,14 @@ export type Role = { createdAt: Scalars['DateTime']; deleted: Scalars['Boolean']; deletedAt?: Maybe; + description: Scalars['String']; disabled: Scalars['Boolean']; hidden: Scalars['Boolean']; id: Scalars['ID']; name: Scalars['String']; - name_en?: Maybe; pageMenus?: Maybe>; updatedAt?: Maybe; - userRoles: Array; + users?: Maybe>; }; export type SendNotificationInput = { @@ -357,6 +357,19 @@ export type State = { userSettings: Array; }; +export type Subject = { + __typename?: 'Subject'; + archived: Scalars['Boolean']; + createdAt: Scalars['DateTime']; + deleted: Scalars['Boolean']; + deletedAt?: Maybe; + disabled: Scalars['Boolean']; + hidden: Scalars['Boolean']; + id: Scalars['ID']; + name: Scalars['String']; + updatedAt?: Maybe; +}; + export type TerminateSessionInput = { jti: Scalars['String']; }; @@ -389,7 +402,7 @@ export type User = { /** Example field (placeholder) */ image?: Maybe; /** Example field (placeholder) */ - is_terms_accepted: Scalars['String']; + isTermsAccepted: Scalars['String']; /** last login time */ lastLoginAt?: Maybe; /** Last login IP */ @@ -399,6 +412,7 @@ export type User = { /** User notification tokens */ notificationToken?: Maybe>; providerAccountId?: Maybe; + roles?: Maybe>; updatedAt?: Maybe; userSetting: UserSetting; /** Example field (username) */ @@ -489,20 +503,6 @@ export type UserDevice = { userAgent: UserAgent; }; -export type UserRole = { - __typename?: 'UserRole'; - archived: Scalars['Boolean']; - createdAt: Scalars['DateTime']; - deleted: Scalars['Boolean']; - deletedAt?: Maybe; - disabled: Scalars['Boolean']; - hidden: Scalars['Boolean']; - id: Scalars['ID']; - role: Role; - updatedAt?: Maybe; - user: User; -}; - export type UserSetting = { __typename?: 'UserSetting'; archived: Scalars['Boolean']; diff --git a/packages/oku-api/types/schema.d.ts b/packages/oku-api/types/schema.d.ts index 6757d9df..328344d6 100644 --- a/packages/oku-api/types/schema.d.ts +++ b/packages/oku-api/types/schema.d.ts @@ -257,14 +257,14 @@ export type Role = { createdAt: Scalars['DateTime']; deleted: Scalars['Boolean']; deletedAt?: Maybe; + description: Scalars['String']; disabled: Scalars['Boolean']; hidden: Scalars['Boolean']; id: Scalars['ID']; name: Scalars['String']; - name_en?: Maybe; pageMenus?: Maybe>; updatedAt?: Maybe; - userRoles: Array; + users?: Maybe>; }; export type SendNotificationInput = { @@ -356,6 +356,19 @@ export type State = { userSettings: Array; }; +export type Subject = { + __typename?: 'Subject'; + archived: Scalars['Boolean']; + createdAt: Scalars['DateTime']; + deleted: Scalars['Boolean']; + deletedAt?: Maybe; + disabled: Scalars['Boolean']; + hidden: Scalars['Boolean']; + id: Scalars['ID']; + name: Scalars['String']; + updatedAt?: Maybe; +}; + export type TerminateSessionInput = { jti: Scalars['String']; }; @@ -388,7 +401,7 @@ export type User = { /** Example field (placeholder) */ image?: Maybe; /** Example field (placeholder) */ - is_terms_accepted: Scalars['String']; + isTermsAccepted: Scalars['String']; /** last login time */ lastLoginAt?: Maybe; /** Last login IP */ @@ -398,6 +411,7 @@ export type User = { /** User notification tokens */ notificationToken?: Maybe>; providerAccountId?: Maybe; + roles?: Maybe>; updatedAt?: Maybe; userSetting: UserSetting; /** Example field (username) */ @@ -488,20 +502,6 @@ export type UserDevice = { userAgent: UserAgent; }; -export type UserRole = { - __typename?: 'UserRole'; - archived: Scalars['Boolean']; - createdAt: Scalars['DateTime']; - deleted: Scalars['Boolean']; - deletedAt?: Maybe; - disabled: Scalars['Boolean']; - hidden: Scalars['Boolean']; - id: Scalars['ID']; - role: Role; - updatedAt?: Maybe; - user: User; -}; - export type UserSetting = { __typename?: 'UserSetting'; archived: Scalars['Boolean']; diff --git a/packages/oku-api/types/vue-apollo.ts b/packages/oku-api/types/vue-apollo.ts index 6b3b9f8a..987e2d59 100644 --- a/packages/oku-api/types/vue-apollo.ts +++ b/packages/oku-api/types/vue-apollo.ts @@ -261,14 +261,14 @@ export type Role = { createdAt: Scalars['DateTime']; deleted: Scalars['Boolean']; deletedAt?: Maybe; + description: Scalars['String']; disabled: Scalars['Boolean']; hidden: Scalars['Boolean']; id: Scalars['ID']; name: Scalars['String']; - name_en?: Maybe; pageMenus?: Maybe>; updatedAt?: Maybe; - userRoles: Array; + users?: Maybe>; }; export type SendNotificationInput = { @@ -360,6 +360,19 @@ export type State = { userSettings: Array; }; +export type Subject = { + __typename?: 'Subject'; + archived: Scalars['Boolean']; + createdAt: Scalars['DateTime']; + deleted: Scalars['Boolean']; + deletedAt?: Maybe; + disabled: Scalars['Boolean']; + hidden: Scalars['Boolean']; + id: Scalars['ID']; + name: Scalars['String']; + updatedAt?: Maybe; +}; + export type TerminateSessionInput = { jti: Scalars['String']; }; @@ -392,7 +405,7 @@ export type User = { /** Example field (placeholder) */ image?: Maybe; /** Example field (placeholder) */ - is_terms_accepted: Scalars['String']; + isTermsAccepted: Scalars['String']; /** last login time */ lastLoginAt?: Maybe; /** Last login IP */ @@ -402,6 +415,7 @@ export type User = { /** User notification tokens */ notificationToken?: Maybe>; providerAccountId?: Maybe; + roles?: Maybe>; updatedAt?: Maybe; userSetting: UserSetting; /** Example field (username) */ @@ -492,20 +506,6 @@ export type UserDevice = { userAgent: UserAgent; }; -export type UserRole = { - __typename?: 'UserRole'; - archived: Scalars['Boolean']; - createdAt: Scalars['DateTime']; - deleted: Scalars['Boolean']; - deletedAt?: Maybe; - disabled: Scalars['Boolean']; - hidden: Scalars['Boolean']; - id: Scalars['ID']; - role: Role; - updatedAt?: Maybe; - user: User; -}; - export type UserSetting = { __typename?: 'UserSetting'; archived: Scalars['Boolean']; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d97c1ada..52fd7feb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,7 +78,7 @@ importers: specifiers: '@antfu/eslint-config': 0.25.2 '@aws-sdk/client-s3': 3.135.0 - '@casl/ability': 5.4.4 + '@casl/ability': 6.0.0 '@faker-js/faker': 7.3.0 '@liaoliaots/nestjs-redis': 9.0.1 '@mikro-orm/cli': 5.2.4 @@ -165,7 +165,7 @@ importers: uuid: 8.3.2 dependencies: '@aws-sdk/client-s3': 3.135.0 - '@casl/ability': 5.4.4 + '@casl/ability': 6.0.0 '@liaoliaots/nestjs-redis': 9.0.1_h6bfqgqj5wx3la6zxssvp3inaa '@mikro-orm/cli': 5.2.4_fvzle4642mugq3p2q3p2lzqara '@mikro-orm/core': 5.2.4_m4jwxaqo25tt4uacgm2u5gk2ey @@ -3638,8 +3638,8 @@ packages: '@capacitor/core': 4.0.1 dev: false - /@casl/ability/5.4.4: - resolution: {integrity: sha512-7+GOnMUq6q4fqtDDesymBXTS9LSDVezYhFiSJ8Rn3f0aQLeRm7qHn66KWbej4niCOvm0XzNj9jzpkK0yz6hUww==} + /@casl/ability/6.0.0: + resolution: {integrity: sha512-hrL9+SiWVzi1C7fA96eZjuJ9ZEkkHpsBZTmqJTxcUAGju9VdHRqHwZUBYR2EBGxCkWi7WIUZs5AH4kHyL9CcPA==} dependencies: '@ucast/mongo2js': 1.3.3 dev: false