feat: Organization services

This commit is contained in:
sauravdhakal12
2026-02-22 15:47:45 +05:45
parent f4c9174752
commit afed1731d2
42 changed files with 862 additions and 17 deletions

View File

@@ -24,7 +24,12 @@ export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
}); });
} }
if (status === 404) {
exception.message = `${exception.message} not found`;
}
response.status(status).json({ response.status(status).json({
success: false,
message: exception.message, message: exception.message,
statusCode: status, statusCode: status,
}); });

View File

@@ -6,7 +6,7 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { DataResponse, MessageResponse } from 'common/http'; import { DataResponse, MessageResponse } from 'common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { catchError, map } from 'rxjs/operators';
@Injectable() @Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, any> { export class ResponseInterceptor<T> implements NestInterceptor<T, any> {

View File

@@ -0,0 +1,26 @@
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
} from 'class-validator';
export function AtLeastOneField(validationOptions?: ValidationOptions) {
return function (constructor: Function) {
registerDecorator({
name: 'atLeastOneField',
target: constructor,
propertyName: undefined as any, // important for class-level
options: validationOptions,
validator: {
validate(_: any, args: ValidationArguments) {
const object = args.object as Record<string, any>;
return Object.values(object).some((value) => value !== undefined);
},
defaultMessage() {
return 'At least one field must be provided';
},
},
});
};
}

View File

@@ -0,0 +1 @@
export * from './at-least-one-field';

View File

@@ -0,0 +1,6 @@
import { IsUUID } from 'class-validator';
export class UUIDQueryDTO {
@IsUUID()
id: string;
}

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common'; import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AsyncLocalStorage } from 'async_hooks'; import { AsyncLocalStorage } from 'async_hooks';
import { RequestContext } from './request-context.type'; import { RequestContext } from './request-context.type';
@@ -31,7 +31,9 @@ export class RequestContextService {
// Helpers // Helpers
get user() { get user() {
return this.get().user; const user = this.get().user;
if (!user) throw new UnauthorizedException();
return user;
} }
get tx() { get tx() {

View File

@@ -36,6 +36,13 @@ export type EnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel = never> = {
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST
} }
export type EnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel = never> = {
equals?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
in?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
notIn?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
}
export type DateTimeFilter<$PrismaModel = never> = { export type DateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
@@ -95,6 +102,16 @@ export type EnumORGANIZATION_JOIN_REQUESTWithAggregatesFilter<$PrismaModel = nev
_max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> _max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel>
} }
export type EnumORGANIZATION_JOIN_REQUEST_TYPEWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
in?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
notIn?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEWithAggregatesFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel>
_max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel>
}
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = { export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
@@ -220,6 +237,13 @@ export type NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel = never> = {
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST
} }
export type NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel = never> = {
equals?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
in?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
notIn?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
}
export type NestedDateTimeFilter<$PrismaModel = never> = { export type NestedDateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
@@ -283,6 +307,16 @@ export type NestedEnumORGANIZATION_JOIN_REQUESTWithAggregatesFilter<$PrismaModel
_max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel> _max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUESTFilter<$PrismaModel>
} }
export type NestedEnumORGANIZATION_JOIN_REQUEST_TYPEWithAggregatesFilter<$PrismaModel = never> = {
equals?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
in?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
notIn?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE[] | Prisma.ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel>
not?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEWithAggregatesFilter<$PrismaModel> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel>
_max?: Prisma.NestedEnumORGANIZATION_JOIN_REQUEST_TYPEFilter<$PrismaModel>
}
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = { export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>

View File

@@ -18,9 +18,18 @@ export const ORGANIZATION_JOIN_REQUEST = {
export type ORGANIZATION_JOIN_REQUEST = (typeof ORGANIZATION_JOIN_REQUEST)[keyof typeof ORGANIZATION_JOIN_REQUEST] export type ORGANIZATION_JOIN_REQUEST = (typeof ORGANIZATION_JOIN_REQUEST)[keyof typeof ORGANIZATION_JOIN_REQUEST]
export const ORGANIZATION_JOIN_REQUEST_TYPE = {
INVITED: 'INVITED',
REQUESTED: 'REQUESTED'
} as const
export type ORGANIZATION_JOIN_REQUEST_TYPE = (typeof ORGANIZATION_JOIN_REQUEST_TYPE)[keyof typeof ORGANIZATION_JOIN_REQUEST_TYPE]
export const ORG_ROLE = { export const ORG_ROLE = {
owner: 'owner',
admin: 'admin', admin: 'admin',
user: 'user' member: 'member'
} as const } as const
export type ORG_ROLE = (typeof ORG_ROLE)[keyof typeof ORG_ROLE] export type ORG_ROLE = (typeof ORG_ROLE)[keyof typeof ORG_ROLE]
@@ -28,7 +37,7 @@ export type ORG_ROLE = (typeof ORG_ROLE)[keyof typeof ORG_ROLE]
export const USER_ROLE = { export const USER_ROLE = {
superadmin: 'superadmin', superadmin: 'superadmin',
ordinary: 'ordinary' user: 'user'
} as const } as const
export type USER_ROLE = (typeof USER_ROLE)[keyof typeof USER_ROLE] export type USER_ROLE = (typeof USER_ROLE)[keyof typeof USER_ROLE]

View File

@@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
"clientVersion": "7.3.0", "clientVersion": "7.3.0",
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735", "engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
"activeProvider": "postgresql", "activeProvider": "postgresql",
"inlineSchema": "model OrganizationJoinRequest {\n userId String\n orgId String\n status ORGANIZATION_JOIN_REQUEST @default(PENDING)\n requestedOn DateTime @default(now())\n rejectReason String?\n\n @@unique([userId, orgId])\n @@map(\"organization_join_request\")\n}\n\nenum ORGANIZATION_JOIN_REQUEST {\n PENDING\n ACCEPTED\n REJECTED\n}\n\nmodel OrganizationUserJoinTable {\n userId String\n orgId String\n role ORG_ROLE @default(user)\n joinedDate DateTime @default(now())\n\n @@unique([userId, orgId])\n @@map(\"organization_user_join\")\n}\n\nenum ORG_ROLE {\n admin\n user\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n description String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"organization\")\n}\n\n// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n firstName String\n middleName String?\n lastName String\n email String @unique\n password String\n role USER_ROLE @default(ordinary)\n isVerified Boolean? @default(false) // TODO: Email using queue\n refreshToken String?\n profilePicture String?\n isDeleted Boolean? @default(false)\n deletedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"user\")\n}\n\nenum USER_ROLE {\n superadmin\n ordinary\n}\n", "inlineSchema": "model OrganizationJoinRequest {\n userId String\n orgId String\n status ORGANIZATION_JOIN_REQUEST @default(PENDING)\n requestType ORGANIZATION_JOIN_REQUEST_TYPE\n requestedOn DateTime @default(now())\n rejectReason String?\n\n @@unique([userId, orgId])\n @@map(\"organization_join_request\")\n}\n\nenum ORGANIZATION_JOIN_REQUEST {\n PENDING\n ACCEPTED\n REJECTED\n}\n\nenum ORGANIZATION_JOIN_REQUEST_TYPE {\n INVITED\n REQUESTED\n}\n\nmodel OrganizationUserJoinTable {\n userId String\n orgId String\n role ORG_ROLE @default(member)\n joinedDate DateTime @default(now())\n\n @@unique([userId, orgId])\n @@map(\"organization_user_join\")\n}\n\nenum ORG_ROLE {\n owner\n admin\n member\n}\n\nmodel Organization {\n id String @id @default(uuid())\n name String\n description String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"organization\")\n}\n\n// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\n// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?\n// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel User {\n id String @id @default(uuid())\n firstName String\n middleName String?\n lastName String\n email String @unique\n password String\n role USER_ROLE @default(user)\n isVerified Boolean? @default(false) // TODO: Email using queue\n refreshToken String?\n profilePicture String?\n isDeleted Boolean? @default(false)\n deletedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"user\")\n}\n\nenum USER_ROLE {\n superadmin\n user\n}\n",
"runtimeDataModel": { "runtimeDataModel": {
"models": {}, "models": {},
"enums": {}, "enums": {},
@@ -28,7 +28,7 @@ const config: runtime.GetPrismaClientConfig = {
} }
} }
config.runtimeDataModel = JSON.parse("{\"models\":{\"OrganizationJoinRequest\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST\"},{\"name\":\"requestedOn\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"rejectReason\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"organization_join_request\"},\"OrganizationUserJoinTable\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"joinedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"organization_user_join\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"organization\"},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"middleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"USER_ROLE\"},{\"name\":\"isVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profilePicture\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isDeleted\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"user\"}},\"enums\":{},\"types\":{}}") config.runtimeDataModel = JSON.parse("{\"models\":{\"OrganizationJoinRequest\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST\"},{\"name\":\"requestType\",\"kind\":\"enum\",\"type\":\"ORGANIZATION_JOIN_REQUEST_TYPE\"},{\"name\":\"requestedOn\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"rejectReason\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":\"organization_join_request\"},\"OrganizationUserJoinTable\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"orgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"ORG_ROLE\"},{\"name\":\"joinedDate\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"organization_user_join\"},\"Organization\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"organization\"},\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"firstName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"middleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"role\",\"kind\":\"enum\",\"type\":\"USER_ROLE\"},{\"name\":\"isVerified\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"profilePicture\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isDeleted\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"deletedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":\"user\"}},\"enums\":{},\"types\":{}}")
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> { async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
const { Buffer } = await import('node:buffer') const { Buffer } = await import('node:buffer')

View File

@@ -746,6 +746,7 @@ export const OrganizationJoinRequestScalarFieldEnum = {
userId: 'userId', userId: 'userId',
orgId: 'orgId', orgId: 'orgId',
status: 'status', status: 'status',
requestType: 'requestType',
requestedOn: 'requestedOn', requestedOn: 'requestedOn',
rejectReason: 'rejectReason' rejectReason: 'rejectReason'
} as const } as const
@@ -852,6 +853,20 @@ export type ListEnumORGANIZATION_JOIN_REQUESTFieldRefInput<$PrismaModel> = Field
/**
* Reference to a field of type 'ORGANIZATION_JOIN_REQUEST_TYPE'
*/
export type EnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'ORGANIZATION_JOIN_REQUEST_TYPE'>
/**
* Reference to a field of type 'ORGANIZATION_JOIN_REQUEST_TYPE[]'
*/
export type ListEnumORGANIZATION_JOIN_REQUEST_TYPEFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'ORGANIZATION_JOIN_REQUEST_TYPE[]'>
/** /**
* Reference to a field of type 'DateTime' * Reference to a field of type 'DateTime'
*/ */

View File

@@ -77,6 +77,7 @@ export const OrganizationJoinRequestScalarFieldEnum = {
userId: 'userId', userId: 'userId',
orgId: 'orgId', orgId: 'orgId',
status: 'status', status: 'status',
requestType: 'requestType',
requestedOn: 'requestedOn', requestedOn: 'requestedOn',
rejectReason: 'rejectReason' rejectReason: 'rejectReason'
} as const } as const

View File

@@ -28,6 +28,7 @@ export type OrganizationJoinRequestMinAggregateOutputType = {
userId: string | null userId: string | null
orgId: string | null orgId: string | null
status: $Enums.ORGANIZATION_JOIN_REQUEST | null status: $Enums.ORGANIZATION_JOIN_REQUEST | null
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | null
requestedOn: Date | null requestedOn: Date | null
rejectReason: string | null rejectReason: string | null
} }
@@ -36,6 +37,7 @@ export type OrganizationJoinRequestMaxAggregateOutputType = {
userId: string | null userId: string | null
orgId: string | null orgId: string | null
status: $Enums.ORGANIZATION_JOIN_REQUEST | null status: $Enums.ORGANIZATION_JOIN_REQUEST | null
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE | null
requestedOn: Date | null requestedOn: Date | null
rejectReason: string | null rejectReason: string | null
} }
@@ -44,6 +46,7 @@ export type OrganizationJoinRequestCountAggregateOutputType = {
userId: number userId: number
orgId: number orgId: number
status: number status: number
requestType: number
requestedOn: number requestedOn: number
rejectReason: number rejectReason: number
_all: number _all: number
@@ -54,6 +57,7 @@ export type OrganizationJoinRequestMinAggregateInputType = {
userId?: true userId?: true
orgId?: true orgId?: true
status?: true status?: true
requestType?: true
requestedOn?: true requestedOn?: true
rejectReason?: true rejectReason?: true
} }
@@ -62,6 +66,7 @@ export type OrganizationJoinRequestMaxAggregateInputType = {
userId?: true userId?: true
orgId?: true orgId?: true
status?: true status?: true
requestType?: true
requestedOn?: true requestedOn?: true
rejectReason?: true rejectReason?: true
} }
@@ -70,6 +75,7 @@ export type OrganizationJoinRequestCountAggregateInputType = {
userId?: true userId?: true
orgId?: true orgId?: true
status?: true status?: true
requestType?: true
requestedOn?: true requestedOn?: true
rejectReason?: true rejectReason?: true
_all?: true _all?: true
@@ -151,6 +157,7 @@ export type OrganizationJoinRequestGroupByOutputType = {
userId: string userId: string
orgId: string orgId: string
status: $Enums.ORGANIZATION_JOIN_REQUEST status: $Enums.ORGANIZATION_JOIN_REQUEST
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn: Date requestedOn: Date
rejectReason: string | null rejectReason: string | null
_count: OrganizationJoinRequestCountAggregateOutputType | null _count: OrganizationJoinRequestCountAggregateOutputType | null
@@ -180,6 +187,7 @@ export type OrganizationJoinRequestWhereInput = {
userId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string userId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string
orgId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string orgId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string requestedOn?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
} }
@@ -188,6 +196,7 @@ export type OrganizationJoinRequestOrderByWithRelationInput = {
userId?: Prisma.SortOrder userId?: Prisma.SortOrder
orgId?: Prisma.SortOrder orgId?: Prisma.SortOrder
status?: Prisma.SortOrder status?: Prisma.SortOrder
requestType?: Prisma.SortOrder
requestedOn?: Prisma.SortOrder requestedOn?: Prisma.SortOrder
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
} }
@@ -200,6 +209,7 @@ export type OrganizationJoinRequestWhereUniqueInput = Prisma.AtLeast<{
userId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string userId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string
orgId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string orgId?: Prisma.StringFilter<"OrganizationJoinRequest"> | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string requestedOn?: Prisma.DateTimeFilter<"OrganizationJoinRequest"> | Date | string
rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null rejectReason?: Prisma.StringNullableFilter<"OrganizationJoinRequest"> | string | null
}, "userId_orgId"> }, "userId_orgId">
@@ -208,6 +218,7 @@ export type OrganizationJoinRequestOrderByWithAggregationInput = {
userId?: Prisma.SortOrder userId?: Prisma.SortOrder
orgId?: Prisma.SortOrder orgId?: Prisma.SortOrder
status?: Prisma.SortOrder status?: Prisma.SortOrder
requestType?: Prisma.SortOrder
requestedOn?: Prisma.SortOrder requestedOn?: Prisma.SortOrder
rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder rejectReason?: Prisma.SortOrderInput | Prisma.SortOrder
_count?: Prisma.OrganizationJoinRequestCountOrderByAggregateInput _count?: Prisma.OrganizationJoinRequestCountOrderByAggregateInput
@@ -222,6 +233,7 @@ export type OrganizationJoinRequestScalarWhereWithAggregatesInput = {
userId?: Prisma.StringWithAggregatesFilter<"OrganizationJoinRequest"> | string userId?: Prisma.StringWithAggregatesFilter<"OrganizationJoinRequest"> | string
orgId?: Prisma.StringWithAggregatesFilter<"OrganizationJoinRequest"> | string orgId?: Prisma.StringWithAggregatesFilter<"OrganizationJoinRequest"> | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTWithAggregatesFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTWithAggregatesFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEWithAggregatesFilter<"OrganizationJoinRequest"> | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeWithAggregatesFilter<"OrganizationJoinRequest"> | Date | string requestedOn?: Prisma.DateTimeWithAggregatesFilter<"OrganizationJoinRequest"> | Date | string
rejectReason?: Prisma.StringNullableWithAggregatesFilter<"OrganizationJoinRequest"> | string | null rejectReason?: Prisma.StringNullableWithAggregatesFilter<"OrganizationJoinRequest"> | string | null
} }
@@ -230,6 +242,7 @@ export type OrganizationJoinRequestCreateInput = {
userId: string userId: string
orgId: string orgId: string
status?: $Enums.ORGANIZATION_JOIN_REQUEST status?: $Enums.ORGANIZATION_JOIN_REQUEST
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Date | string requestedOn?: Date | string
rejectReason?: string | null rejectReason?: string | null
} }
@@ -238,6 +251,7 @@ export type OrganizationJoinRequestUncheckedCreateInput = {
userId: string userId: string
orgId: string orgId: string
status?: $Enums.ORGANIZATION_JOIN_REQUEST status?: $Enums.ORGANIZATION_JOIN_REQUEST
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Date | string requestedOn?: Date | string
rejectReason?: string | null rejectReason?: string | null
} }
@@ -246,6 +260,7 @@ export type OrganizationJoinRequestUpdateInput = {
userId?: Prisma.StringFieldUpdateOperationsInput | string userId?: Prisma.StringFieldUpdateOperationsInput | string
orgId?: Prisma.StringFieldUpdateOperationsInput | string orgId?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
} }
@@ -254,6 +269,7 @@ export type OrganizationJoinRequestUncheckedUpdateInput = {
userId?: Prisma.StringFieldUpdateOperationsInput | string userId?: Prisma.StringFieldUpdateOperationsInput | string
orgId?: Prisma.StringFieldUpdateOperationsInput | string orgId?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
} }
@@ -262,6 +278,7 @@ export type OrganizationJoinRequestCreateManyInput = {
userId: string userId: string
orgId: string orgId: string
status?: $Enums.ORGANIZATION_JOIN_REQUEST status?: $Enums.ORGANIZATION_JOIN_REQUEST
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Date | string requestedOn?: Date | string
rejectReason?: string | null rejectReason?: string | null
} }
@@ -270,6 +287,7 @@ export type OrganizationJoinRequestUpdateManyMutationInput = {
userId?: Prisma.StringFieldUpdateOperationsInput | string userId?: Prisma.StringFieldUpdateOperationsInput | string
orgId?: Prisma.StringFieldUpdateOperationsInput | string orgId?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
} }
@@ -278,6 +296,7 @@ export type OrganizationJoinRequestUncheckedUpdateManyInput = {
userId?: Prisma.StringFieldUpdateOperationsInput | string userId?: Prisma.StringFieldUpdateOperationsInput | string
orgId?: Prisma.StringFieldUpdateOperationsInput | string orgId?: Prisma.StringFieldUpdateOperationsInput | string
status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST status?: Prisma.EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST
requestType?: Prisma.EnumORGANIZATION_JOIN_REQUEST_TYPEFieldUpdateOperationsInput | $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string requestedOn?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null rejectReason?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
} }
@@ -291,6 +310,7 @@ export type OrganizationJoinRequestCountOrderByAggregateInput = {
userId?: Prisma.SortOrder userId?: Prisma.SortOrder
orgId?: Prisma.SortOrder orgId?: Prisma.SortOrder
status?: Prisma.SortOrder status?: Prisma.SortOrder
requestType?: Prisma.SortOrder
requestedOn?: Prisma.SortOrder requestedOn?: Prisma.SortOrder
rejectReason?: Prisma.SortOrder rejectReason?: Prisma.SortOrder
} }
@@ -299,6 +319,7 @@ export type OrganizationJoinRequestMaxOrderByAggregateInput = {
userId?: Prisma.SortOrder userId?: Prisma.SortOrder
orgId?: Prisma.SortOrder orgId?: Prisma.SortOrder
status?: Prisma.SortOrder status?: Prisma.SortOrder
requestType?: Prisma.SortOrder
requestedOn?: Prisma.SortOrder requestedOn?: Prisma.SortOrder
rejectReason?: Prisma.SortOrder rejectReason?: Prisma.SortOrder
} }
@@ -307,6 +328,7 @@ export type OrganizationJoinRequestMinOrderByAggregateInput = {
userId?: Prisma.SortOrder userId?: Prisma.SortOrder
orgId?: Prisma.SortOrder orgId?: Prisma.SortOrder
status?: Prisma.SortOrder status?: Prisma.SortOrder
requestType?: Prisma.SortOrder
requestedOn?: Prisma.SortOrder requestedOn?: Prisma.SortOrder
rejectReason?: Prisma.SortOrder rejectReason?: Prisma.SortOrder
} }
@@ -319,6 +341,10 @@ export type EnumORGANIZATION_JOIN_REQUESTFieldUpdateOperationsInput = {
set?: $Enums.ORGANIZATION_JOIN_REQUEST set?: $Enums.ORGANIZATION_JOIN_REQUEST
} }
export type EnumORGANIZATION_JOIN_REQUEST_TYPEFieldUpdateOperationsInput = {
set?: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
}
export type DateTimeFieldUpdateOperationsInput = { export type DateTimeFieldUpdateOperationsInput = {
set?: Date | string set?: Date | string
} }
@@ -333,6 +359,7 @@ export type OrganizationJoinRequestSelect<ExtArgs extends runtime.Types.Extensio
userId?: boolean userId?: boolean
orgId?: boolean orgId?: boolean
status?: boolean status?: boolean
requestType?: boolean
requestedOn?: boolean requestedOn?: boolean
rejectReason?: boolean rejectReason?: boolean
}, ExtArgs["result"]["organizationJoinRequest"]> }, ExtArgs["result"]["organizationJoinRequest"]>
@@ -341,6 +368,7 @@ export type OrganizationJoinRequestSelectCreateManyAndReturn<ExtArgs extends run
userId?: boolean userId?: boolean
orgId?: boolean orgId?: boolean
status?: boolean status?: boolean
requestType?: boolean
requestedOn?: boolean requestedOn?: boolean
rejectReason?: boolean rejectReason?: boolean
}, ExtArgs["result"]["organizationJoinRequest"]> }, ExtArgs["result"]["organizationJoinRequest"]>
@@ -349,6 +377,7 @@ export type OrganizationJoinRequestSelectUpdateManyAndReturn<ExtArgs extends run
userId?: boolean userId?: boolean
orgId?: boolean orgId?: boolean
status?: boolean status?: boolean
requestType?: boolean
requestedOn?: boolean requestedOn?: boolean
rejectReason?: boolean rejectReason?: boolean
}, ExtArgs["result"]["organizationJoinRequest"]> }, ExtArgs["result"]["organizationJoinRequest"]>
@@ -357,11 +386,12 @@ export type OrganizationJoinRequestSelectScalar = {
userId?: boolean userId?: boolean
orgId?: boolean orgId?: boolean
status?: boolean status?: boolean
requestType?: boolean
requestedOn?: boolean requestedOn?: boolean
rejectReason?: boolean rejectReason?: boolean
} }
export type OrganizationJoinRequestOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"userId" | "orgId" | "status" | "requestedOn" | "rejectReason", ExtArgs["result"]["organizationJoinRequest"]> export type OrganizationJoinRequestOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"userId" | "orgId" | "status" | "requestType" | "requestedOn" | "rejectReason", ExtArgs["result"]["organizationJoinRequest"]>
export type $OrganizationJoinRequestPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = { export type $OrganizationJoinRequestPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
name: "OrganizationJoinRequest" name: "OrganizationJoinRequest"
@@ -370,6 +400,7 @@ export type $OrganizationJoinRequestPayload<ExtArgs extends runtime.Types.Extens
userId: string userId: string
orgId: string orgId: string
status: $Enums.ORGANIZATION_JOIN_REQUEST status: $Enums.ORGANIZATION_JOIN_REQUEST
requestType: $Enums.ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn: Date requestedOn: Date
rejectReason: string | null rejectReason: string | null
}, ExtArgs["result"]["organizationJoinRequest"]> }, ExtArgs["result"]["organizationJoinRequest"]>
@@ -798,6 +829,7 @@ export interface OrganizationJoinRequestFieldRefs {
readonly userId: Prisma.FieldRef<"OrganizationJoinRequest", 'String'> readonly userId: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
readonly orgId: Prisma.FieldRef<"OrganizationJoinRequest", 'String'> readonly orgId: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
readonly status: Prisma.FieldRef<"OrganizationJoinRequest", 'ORGANIZATION_JOIN_REQUEST'> readonly status: Prisma.FieldRef<"OrganizationJoinRequest", 'ORGANIZATION_JOIN_REQUEST'>
readonly requestType: Prisma.FieldRef<"OrganizationJoinRequest", 'ORGANIZATION_JOIN_REQUEST_TYPE'>
readonly requestedOn: Prisma.FieldRef<"OrganizationJoinRequest", 'DateTime'> readonly requestedOn: Prisma.FieldRef<"OrganizationJoinRequest", 'DateTime'>
readonly rejectReason: Prisma.FieldRef<"OrganizationJoinRequest", 'String'> readonly rejectReason: Prisma.FieldRef<"OrganizationJoinRequest", 'String'>
} }

View File

@@ -0,0 +1,35 @@
/*
Warnings:
- The values [user] on the enum `ORG_ROLE` will be removed. If these variants are still used in the database, this will fail.
- The values [ordinary] on the enum `USER_ROLE` will be removed. If these variants are still used in the database, this will fail.
*/
-- AlterEnum
BEGIN;
CREATE TYPE "ORG_ROLE_new" AS ENUM ('owner', 'admin', 'member');
ALTER TABLE "public"."organization_user_join" ALTER COLUMN "role" DROP DEFAULT;
ALTER TABLE "organization_user_join" ALTER COLUMN "role" TYPE "ORG_ROLE_new" USING ("role"::text::"ORG_ROLE_new");
ALTER TYPE "ORG_ROLE" RENAME TO "ORG_ROLE_old";
ALTER TYPE "ORG_ROLE_new" RENAME TO "ORG_ROLE";
DROP TYPE "public"."ORG_ROLE_old";
ALTER TABLE "organization_user_join" ALTER COLUMN "role" SET DEFAULT 'member';
COMMIT;
-- AlterEnum
BEGIN;
CREATE TYPE "USER_ROLE_new" AS ENUM ('superadmin', 'user');
ALTER TABLE "public"."user" ALTER COLUMN "role" DROP DEFAULT;
ALTER TABLE "user" ALTER COLUMN "role" TYPE "USER_ROLE_new" USING ("role"::text::"USER_ROLE_new");
ALTER TYPE "USER_ROLE" RENAME TO "USER_ROLE_old";
ALTER TYPE "USER_ROLE_new" RENAME TO "USER_ROLE";
DROP TYPE "public"."USER_ROLE_old";
ALTER TABLE "user" ALTER COLUMN "role" SET DEFAULT 'user';
COMMIT;
-- AlterTable
ALTER TABLE "organization_user_join" ALTER COLUMN "role" SET DEFAULT 'member';
-- AlterTable
ALTER TABLE "user" ALTER COLUMN "role" SET DEFAULT 'user';

View File

@@ -0,0 +1 @@
-- This is an empty migration.

View File

@@ -0,0 +1,11 @@
/*
Warnings:
- Added the required column `requestType` to the `organization_join_request` table without a default value. This is not possible if the table is not empty.
*/
-- CreateEnum
CREATE TYPE "ORGANIZATION_JOIN_REQUEST_TYPE" AS ENUM ('INVITED', 'REQUESTED');
-- AlterTable
ALTER TABLE "organization_join_request" ADD COLUMN "requestType" "ORGANIZATION_JOIN_REQUEST_TYPE" NOT NULL;

View File

@@ -2,7 +2,9 @@ model OrganizationJoinRequest {
userId String userId String
orgId String orgId String
status ORGANIZATION_JOIN_REQUEST @default(PENDING) status ORGANIZATION_JOIN_REQUEST @default(PENDING)
requestType ORGANIZATION_JOIN_REQUEST_TYPE
requestedOn DateTime @default(now()) requestedOn DateTime @default(now())
updatedAt DateTime @updatedAt
rejectReason String? rejectReason String?
@@unique([userId, orgId]) @@unique([userId, orgId])
@@ -14,3 +16,8 @@ enum ORGANIZATION_JOIN_REQUEST {
ACCEPTED ACCEPTED
REJECTED REJECTED
} }
enum ORGANIZATION_JOIN_REQUEST_TYPE {
INVITED
REQUESTED
}

View File

@@ -1,7 +1,7 @@
model OrganizationUserJoinTable { model OrganizationUserJoinTable {
userId String userId String
orgId String orgId String
role ORG_ROLE @default(user) role ORG_ROLE @default(member)
joinedDate DateTime @default(now()) joinedDate DateTime @default(now())
@@unique([userId, orgId]) @@unique([userId, orgId])
@@ -9,6 +9,7 @@ model OrganizationUserJoinTable {
} }
enum ORG_ROLE { enum ORG_ROLE {
owner
admin admin
user member
} }

View File

@@ -5,7 +5,7 @@ model User {
lastName String lastName String
email String @unique email String @unique
password String password String
role USER_ROLE @default(ordinary) role USER_ROLE @default(user)
isVerified Boolean? @default(false) // TODO: Email using queue isVerified Boolean? @default(false) // TODO: Email using queue
refreshToken String? refreshToken String?
profilePicture String? profilePicture String?
@@ -20,5 +20,5 @@ model User {
enum USER_ROLE { enum USER_ROLE {
superadmin superadmin
ordinary user
} }

View File

@@ -11,6 +11,9 @@ import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { ResponseInterceptor } from 'common/interceptors/response.interceptor'; import { ResponseInterceptor } from 'common/interceptors/response.interceptor';
import { ExceptionsHandler } from '@nestjs/core/exceptions/exceptions-handler'; import { ExceptionsHandler } from '@nestjs/core/exceptions/exceptions-handler';
import { HttpExceptionFilter } from 'common/exceptions/exception-filter'; import { HttpExceptionFilter } from 'common/exceptions/exception-filter';
import { OrganizationModule } from './organization/organization.module';
import { OrganizationMembershipModule } from './organization-membership/organization-membership.module';
import { AuthorizationModule } from './authorization/authorization.module';
@Module({ @Module({
imports: [ imports: [
@@ -21,6 +24,9 @@ import { HttpExceptionFilter } from 'common/exceptions/exception-filter';
AuthModule, AuthModule,
RequestContextModule, RequestContextModule,
PrismaModule, PrismaModule,
OrganizationModule,
OrganizationMembershipModule,
AuthorizationModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [ providers: [

View File

@@ -15,8 +15,10 @@ import {
} from './dto'; } from './dto';
import { Response } from 'express'; import { Response } from 'express';
import { DataResponse } from 'common/http'; import { DataResponse } from 'common/http';
import { Public } from './decorators';
@Controller('auth') @Controller('auth')
@Public()
export class AuthController { export class AuthController {
constructor(private readonly authService: AuthService) {} constructor(private readonly authService: AuthService) {}

View File

@@ -13,8 +13,7 @@ import { RequestContextModule } from 'core/als/request-context.module';
AuthService, AuthService,
{ {
provide: APP_GUARD, provide: APP_GUARD,
useFactory: () => AuthGuard, useClass: AuthGuard,
inject: [Reflector],
}, },
], ],
controllers: [AuthController], controllers: [AuthController],

View File

@@ -1,6 +1,7 @@
import { import {
CanActivate, CanActivate,
ExecutionContext, ExecutionContext,
Injectable,
UnauthorizedException, UnauthorizedException,
} from '@nestjs/common'; } from '@nestjs/common';
import { RequestContextService } from 'core/als/request-context.service'; import { RequestContextService } from 'core/als/request-context.service';
@@ -10,6 +11,7 @@ import { Request } from 'express';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { PUBLIC_KEY } from 'common/keys'; import { PUBLIC_KEY } from 'common/keys';
@Injectable()
export class AuthGuard implements CanActivate { export class AuthGuard implements CanActivate {
constructor( constructor(
private readonly reflector: Reflector, private readonly reflector: Reflector,
@@ -30,7 +32,9 @@ export class AuthGuard implements CanActivate {
if (!token) throw new UnauthorizedException(); if (!token) throw new UnauthorizedException();
try { try {
const payload: JwtPayload = await this.jwtService.verifyAsync(token); const payload: JwtPayload = await this.jwtService.verifyAsync(token, {
secret: 'demo',
});
this.requestContext.set('user', payload); this.requestContext.set('user', payload);
return true; return true;

View File

@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { AuthorizationService } from './authorization.service';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
providers: [AuthorizationService],
imports: [PrismaModule],
exports: [AuthorizationService],
})
export class AuthorizationModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthorizationService } from './authorization.service';
describe('AuthorizationService', () => {
let service: AuthorizationService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthorizationService],
}).compile();
service = module.get<AuthorizationService>(AuthorizationService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,71 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { USER_ORGANIZATION_OPERATIONS } from './operations';
import { ORG_ROLE } from 'prisma/generated/prisma/enums';
@Injectable()
export class AuthorizationService {
constructor(private readonly prisma: PrismaService) {}
// can perform operation
async canPerformOperation(
userId: string,
orgId: string,
operation: USER_ORGANIZATION_OPERATIONS,
) {
switch (operation) {
case USER_ORGANIZATION_OPERATIONS.DELETE_ORGANIZATION:
return await this.isOwner(userId, orgId);
case USER_ORGANIZATION_OPERATIONS.UPDATE_ORGANIZATION:
return await this.isOwner(userId, orgId);
case USER_ORGANIZATION_OPERATIONS.INVITE_USERS:
return await this.canInvite(userId, orgId);
}
}
private async isOwner(userId: string, orgId: string) {
const isUserPartOfOrganization = await this.isUserPartOfOrganization(
userId,
orgId,
);
return isUserPartOfOrganization
? isUserPartOfOrganization.role === ORG_ROLE.owner
: !!isUserPartOfOrganization;
}
private async canInvite(userId: string, orgId: string) {
const isUserPartOfOrganization = await this.isUserPartOfOrganization(
userId,
orgId,
);
return isUserPartOfOrganization
? isUserPartOfOrganization.role === ORG_ROLE.admin ||
isUserPartOfOrganization.role === ORG_ROLE.owner
: !!isUserPartOfOrganization;
}
private async isAdmin(userId: string, orgId: string) {
const isUserPartOfOrganization = await this.isUserPartOfOrganization(
userId,
orgId,
);
return isUserPartOfOrganization
? isUserPartOfOrganization.role === ORG_ROLE.admin
: !!isUserPartOfOrganization;
}
// HELPER FUNCTION
private async isUserPartOfOrganization(userId: string, orgId: string) {
return await this.prisma.organizationUserJoinTable.findUnique({
where: {
userId_orgId: {
orgId,
userId,
},
},
});
}
}

View File

@@ -0,0 +1,5 @@
export enum USER_ORGANIZATION_OPERATIONS {
UPDATE_ORGANIZATION = 'update_organization',
DELETE_ORGANIZATION = 'delete_organization',
INVITE_USERS = 'invite_users',
}

View File

@@ -0,0 +1 @@
export * from './invite-to-org.dto';

View File

@@ -0,0 +1,16 @@
import { IsEnum, IsNotEmpty, IsUUID } from 'class-validator';
import { ORG_ROLE } from 'prisma/generated/prisma/enums';
export class InviteUserToOrganizationRequestDTO {
@IsUUID()
@IsNotEmpty()
invitedUserId: string;
@IsUUID()
@IsNotEmpty()
orgId: string;
@IsEnum(ORG_ROLE)
@IsNotEmpty()
role: ORG_ROLE;
}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { OrganizationMembershipController } from './organization-membership.controller';
describe('OrganizationMembershipController', () => {
let controller: OrganizationMembershipController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [OrganizationMembershipController],
}).compile();
controller = module.get<OrganizationMembershipController>(OrganizationMembershipController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,4 @@
import { Controller } from '@nestjs/common';
@Controller('organization-membership')
export class OrganizationMembershipController {}

View File

@@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { OrganizationMembershipController } from './organization-membership.controller';
import { OrganizationMembershipService } from './organization-membership.service';
import { OrganizationModule } from 'src/organization/organization.module';
import { UserModule } from 'src/user/user.module';
import { PrismaModule } from 'src/prisma/prisma.module';
import { AuthorizationModule } from 'src/authorization/authorization.module';
@Module({
controllers: [OrganizationMembershipController],
providers: [OrganizationMembershipService],
imports: [OrganizationModule, UserModule, PrismaModule, AuthorizationModule],
})
export class OrganizationMembershipModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { OrganizationMembershipService } from './organization-membership.service';
describe('OrganizationMembershipService', () => {
let service: OrganizationMembershipService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [OrganizationMembershipService],
}).compile();
service = module.get<OrganizationMembershipService>(OrganizationMembershipService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,148 @@
import {
BadRequestException,
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { OrganizationService } from 'src/organization/organization.service';
import { UserService } from 'src/user/user.service';
import { InviteUserToOrganizationRequestDTO } from './dto';
import { PrismaService } from 'src/prisma/prisma.service';
import {
ORGANIZATION_JOIN_REQUEST,
ORGANIZATION_JOIN_REQUEST_TYPE,
} from 'prisma/generated/prisma/enums';
import { AuthorizationService } from 'src/authorization/authorization.service';
import { USER_ORGANIZATION_OPERATIONS } from 'src/authorization/operations';
import { Prisma } from 'prisma/generated/prisma/client';
@Injectable()
export class OrganizationMembershipService {
constructor(
private readonly userService: UserService,
private readonly orgService: OrganizationService,
private readonly prisma: PrismaService,
private readonly authorization: AuthorizationService,
) {}
async inviteUserToOrg(
userId: string,
dto: InviteUserToOrganizationRequestDTO,
) {
const [orgExists, invitedUser] = await Promise.all([
this.orgService.findById(dto.orgId),
this.userService.getById(dto.invitedUserId),
]);
if (!orgExists) throw new NotFoundException('Organization');
if (!invitedUser) throw new NotFoundException('User');
const userAlreadyPart =
await this.prisma.organizationUserJoinTable.findUnique({
where: {
userId_orgId: {
orgId: dto.orgId,
userId,
},
},
});
if (userAlreadyPart)
throw new BadRequestException('User already part of this organization');
const canInviteUser = await this.authorization.canPerformOperation(
userId,
dto.orgId,
USER_ORGANIZATION_OPERATIONS.INVITE_USERS,
);
if (!canInviteUser) throw new ForbiddenException('Insufficient Permission');
try {
const invitation = await this.prisma.organizationJoinRequest.create({
data: {
...dto,
userId: dto.invitedUserId,
requestType: ORGANIZATION_JOIN_REQUEST_TYPE.INVITED,
},
});
return invitation;
} catch (err) {
if (err instanceof Prisma.PrismaClientKnownRequestError) {
if (err.code === 'P2002')
throw new BadRequestException('User invitation already sent.');
}
throw err;
}
}
requestToJoin() {}
// TODO: reject, rejectReason
async acceptInvite(userId: string, orgId: string) {
const orgExists = await this.orgService.findById(orgId);
if (!orgExists) throw new NotFoundException('Organization');
const [userAlreadyPart, isUserInvited] = await Promise.all([
this.prisma.organizationUserJoinTable.findUnique({
where: {
userId_orgId: {
orgId,
userId,
},
},
}),
this.prisma.organizationJoinRequest.findUnique({
where: {
userId_orgId: {
orgId,
userId,
},
status: ORGANIZATION_JOIN_REQUEST.PENDING,
},
}),
]);
if (userAlreadyPart)
throw new BadRequestException('User already part of this organization');
if (!isUserInvited)
throw new BadRequestException(
'User has no invitations from this organization',
);
return await this.prisma.$transaction(async (tx) => {
await tx.organizationJoinRequest.update({
where: {
userId_orgId: {
userId,
orgId,
},
},
data: {
status: ORGANIZATION_JOIN_REQUEST.ACCEPTED,
},
});
return await tx.organizationUserJoinTable.create({
data: {
orgId,
userId,
},
});
});
}
async getUserInvitations(
userId: string,
requestType: ORGANIZATION_JOIN_REQUEST_TYPE,
status: ORGANIZATION_JOIN_REQUEST = ORGANIZATION_JOIN_REQUEST.PENDING,
) {
return await this.prisma.organizationJoinRequest.findMany({
where: {
userId: userId,
status: status,
requestType: requestType,
},
});
}
}

View File

@@ -0,0 +1,2 @@
export * from './organization.dto';
export * from './organization-response.dto';

View File

@@ -0,0 +1,15 @@
import { Organization } from 'prisma/generated/prisma/client';
export class OrganizationDTO {
readonly name: string;
readonly description: string | null;
readonly createdAt: Date;
constructor(organization: Organization) {
this.name = organization.name;
this.description = organization.description;
this.createdAt = organization.createdAt;
}
}
export class CreateNewOrganizationResponseDTO extends OrganizationDTO {}

View File

@@ -0,0 +1,30 @@
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { AtLeastOneField } from 'common/validators';
export class CreateNewOrganizationRequestDTO {
@ApiProperty({
description: "Organization's name",
example: 'Lions',
type: 'string',
})
@IsString()
@IsNotEmpty()
name: string;
@ApiPropertyOptional({
description: 'Short description for organization',
example: 'A cool organization active for over 10 years.',
type: 'string',
})
@IsOptional()
@IsString()
description?: string;
}
@AtLeastOneField({
message: 'Provide at least one field to update',
})
export class UpdateOrganizationRequestDTO extends PartialType(
CreateNewOrganizationRequestDTO,
) {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { OrganizationController } from './organization.controller';
describe('OrganizationController', () => {
let controller: OrganizationController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [OrganizationController],
}).compile();
controller = module.get<OrganizationController>(OrganizationController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@@ -0,0 +1,94 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
} from '@nestjs/common';
import {
CreateNewOrganizationRequestDTO,
OrganizationDTO,
UpdateOrganizationRequestDTO,
} from './dtos';
import { OrganizationService } from './organization.service';
import { RequestContextService } from 'core/als/request-context.service';
import { ApiBearerAuth } from '@nestjs/swagger';
import { DataResponse } from 'common/http';
@Controller('organization')
@ApiBearerAuth('access-token')
export class OrganizationController {
constructor(
private readonly orgService: OrganizationService,
private readonly requestContext: RequestContextService,
) {}
@Post('')
async createNewOrganization(
@Body() body: CreateNewOrganizationRequestDTO,
): Promise<DataResponse<OrganizationDTO>> {
const user = this.requestContext.user;
const newOrg = await this.orgService.createNewOrganization(
user.userId,
body,
);
return new DataResponse<OrganizationDTO>(
new OrganizationDTO(newOrg),
'Organization created successfully.',
);
}
@Get('')
async getOrganizations(): Promise<DataResponse<OrganizationDTO[]>> {
const organizations = await this.orgService.getOrganizations();
return new DataResponse(
organizations.map((organization) => new OrganizationDTO(organization)),
);
}
@Get(':id')
async getAnOrganization(
@Param('id') id: string,
): Promise<DataResponse<OrganizationDTO>> {
const organization = await this.orgService.getOrganizationById(id);
return new DataResponse(new OrganizationDTO(organization));
}
@Put(':id')
async updateAnOrganization(
@Param('id') orgId: string,
@Body() body: UpdateOrganizationRequestDTO,
): Promise<DataResponse<OrganizationDTO>> {
const user = this.requestContext.user;
const updatedOrg = await this.orgService.updateAnOrganization(
user.userId,
orgId,
body,
);
return new DataResponse<OrganizationDTO>(
new OrganizationDTO(updatedOrg),
'Organization updated successfully',
);
}
@Delete(':id')
async deleteAnOrganization(
@Param('id') orgId: string,
): Promise<DataResponse<OrganizationDTO>> {
const user = this.requestContext.user;
const deletedOrg = await this.orgService.deleteAnOrganization(
user.userId,
orgId,
);
return new DataResponse<OrganizationDTO>(
new OrganizationDTO(deletedOrg),
'Organization deleted successfully',
);
}
}

View File

@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { OrganizationController } from './organization.controller';
import { OrganizationService } from './organization.service';
import { PrismaModule } from 'src/prisma/prisma.module';
import { RequestContextModule } from 'core/als/request-context.module';
import { UserModule } from 'src/user/user.module';
import { AuthorizationModule } from 'src/authorization/authorization.module';
@Module({
controllers: [OrganizationController],
providers: [OrganizationService],
imports: [
PrismaModule,
RequestContextModule,
UserModule,
AuthorizationModule,
],
exports: [OrganizationService],
})
export class OrganizationModule {}

View File

@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { OrganizationService } from './organization.service';
describe('OrganizationService', () => {
let service: OrganizationService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [OrganizationService],
}).compile();
service = module.get<OrganizationService>(OrganizationService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@@ -0,0 +1,120 @@
import {
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import {
CreateNewOrganizationRequestDTO,
UpdateOrganizationRequestDTO,
} from './dtos';
import { PrismaService } from 'src/prisma/prisma.service';
import { RequestContextService } from 'core/als/request-context.service';
import { ORG_ROLE } from 'prisma/generated/prisma/enums';
import { AuthorizationService } from 'src/authorization/authorization.service';
import { USER_ORGANIZATION_OPERATIONS } from 'src/authorization/operations';
@Injectable()
export class OrganizationService {
constructor(
private readonly prisma: PrismaService,
// private readonly reqContext: RequestContextService,
private readonly authorization: AuthorizationService,
) {}
async createNewOrganization(
userId: string,
dto: CreateNewOrganizationRequestDTO,
) {
return await this.prisma.$transaction(async (tx) => {
const newOrganization = await tx.organization.create({
data: dto,
});
await tx.organizationUserJoinTable.create({
data: {
orgId: newOrganization.id,
userId: userId,
role: ORG_ROLE.owner,
},
});
return newOrganization;
});
}
// NOTE: Pagination
async getOrganizations() {
return await this.prisma.organization.findMany();
}
async getOrganizationById(orgId: string) {
const orgExists = await this.findById(orgId);
if (!orgExists) throw new NotFoundException('Organization');
return orgExists;
}
async updateAnOrganization(
userId: string,
orgId: string,
dto: UpdateOrganizationRequestDTO,
) {
const orgExists = await this.findById(orgId);
if (!orgExists) throw new NotFoundException('Organization');
const canUserUpdateOrganization =
await this.authorization.canPerformOperation(
userId,
orgId,
USER_ORGANIZATION_OPERATIONS.UPDATE_ORGANIZATION,
);
if (!canUserUpdateOrganization)
throw new ForbiddenException('Not enough permission');
return this.prisma.organization.update({
where: {
id: orgId,
},
data: dto,
});
}
// TODO: Either empty or choose someone to be owner
async deleteAnOrganization(userId: string, orgId: string) {
const orgExists = await this.findById(orgId);
if (!orgExists) throw new NotFoundException('Organization');
const canUserDeleteOrganization =
await this.authorization.canPerformOperation(
userId,
orgId,
USER_ORGANIZATION_OPERATIONS.DELETE_ORGANIZATION,
);
if (!canUserDeleteOrganization)
throw new ForbiddenException('Not enough permission');
return await this.prisma.$transaction(async (tx) => {
const deletedOrganization = await tx.organization.delete({
where: {
id: orgId,
},
});
await tx.organizationUserJoinTable.delete({
where: {
userId_orgId: {
userId,
orgId,
},
},
});
return deletedOrganization;
});
}
async findById(orgId: string) {
return await this.prisma.organization.findUnique({ where: { id: orgId } });
}
}

View File

@@ -28,6 +28,14 @@ export class UserService {
}); });
} }
async getById(id: string) {
return await this.prisma.user.findUnique({
where: {
id: id,
},
});
}
async updateRefreshToken(id: string, refreshToken: string) { async updateRefreshToken(id: string, refreshToken: string) {
return await this.prisma.user.update({ return await this.prisma.user.update({
where: { id }, where: { id },