c6cd4c311d
* chore(serverinvites): repository refactor for multiregion * chore(serverinvites): remove migrated functions from old repository * chore(serverinvites): refactor serverInviteForToken resolver for multiregion * chore(serverinvites): invite processing service refactor for multiregion * chore(serverinvites): subscription refactor for multiregion * chore(serverinvites): move buildEmailContents to dedicated file * chore(serverinvites): deleteAllStreamInvites function multiregion refactor * chore(serverinvites): refactor deleteServerOnlyInvites multiregion repository * chore(serverinvites): complete repository refactor for multiregion * feat(serverinvites): create domain module in server invites * fix(serverinvites): no relative imports * feat(serverinvites): extract individual types from repository * feat(serverinvites): move interfaces to operations * fix(serverinvites): update imports referencing old interfaces file * fix(serverinvites): type mismatch for insert invite and delete old * chore(serverinvites): refactor to single repo function * test(serverinvites): fix tests * fix(serverinvites): use domain types in all places * feat(serverinvites): WIP unity * feat(serverinvites): move to new facory names and types * feat(serverinvites): fix tests * fix(serverinvites): use factory name --------- Co-authored-by: Alessandro Magionami <alessandro.magionami@gmail.com>
156 lines
4.2 KiB
TypeScript
156 lines
4.2 KiB
TypeScript
import {
|
|
AccessRequestCreationError,
|
|
AccessRequestProcessingError
|
|
} from '@/modules/accessrequests/errors'
|
|
import { AccessRequestsEmitter } from '@/modules/accessrequests/events/emitter'
|
|
import { StreamAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes'
|
|
import {
|
|
AccessRequestType,
|
|
createNewRequest,
|
|
deleteRequestById,
|
|
generateId,
|
|
getPendingAccessRequest,
|
|
getPendingAccessRequests,
|
|
getUsersPendingAccessRequest,
|
|
ServerAccessRequestRecord
|
|
} from '@/modules/accessrequests/repositories'
|
|
import { StreamInvalidAccessError } from '@/modules/core/errors/stream'
|
|
import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types'
|
|
import { Roles, StreamRoles } from '@/modules/core/helpers/mainConstants'
|
|
import { getStream } from '@/modules/core/repositories/streams'
|
|
import {
|
|
addOrUpdateStreamCollaborator,
|
|
validateStreamAccess
|
|
} from '@/modules/core/services/streams/streamAccessService'
|
|
import { ensureError } from '@/modules/shared/helpers/errorHelper'
|
|
import { MaybeNullOrUndefined, Nullable } from '@/modules/shared/helpers/typeHelper'
|
|
|
|
function buildStreamAccessRequestGraphQLReturn(
|
|
record: ServerAccessRequestRecord<AccessRequestType.Stream, string>
|
|
): StreamAccessRequestGraphQLReturn {
|
|
return {
|
|
id: record.id,
|
|
requesterId: record.requesterId,
|
|
streamId: record.resourceId,
|
|
createdAt: record.createdAt
|
|
}
|
|
}
|
|
|
|
export async function getUserStreamAccessRequest(
|
|
userId: string,
|
|
streamId: string
|
|
): Promise<Nullable<StreamAccessRequestGraphQLReturn>> {
|
|
const req = await getUsersPendingAccessRequest(
|
|
userId,
|
|
AccessRequestType.Stream,
|
|
streamId
|
|
)
|
|
if (!req) return null
|
|
|
|
return buildStreamAccessRequestGraphQLReturn(req)
|
|
}
|
|
|
|
/**
|
|
* Create new stream access request
|
|
*/
|
|
export async function requestStreamAccess(userId: string, streamId: string) {
|
|
const [stream, existingRequest] = await Promise.all([
|
|
getStream({ userId, streamId }),
|
|
getUserStreamAccessRequest(userId, streamId)
|
|
])
|
|
|
|
if (existingRequest) {
|
|
throw new AccessRequestCreationError(
|
|
'User already has a pending access request for this stream'
|
|
)
|
|
}
|
|
|
|
if (!stream) {
|
|
throw new AccessRequestCreationError(
|
|
"Can't request access to a non-existant stream"
|
|
)
|
|
}
|
|
|
|
if (stream.role) {
|
|
throw new AccessRequestCreationError(
|
|
'User already has access to the specified stream'
|
|
)
|
|
}
|
|
|
|
const req = await createNewRequest<AccessRequestType.Stream, string>({
|
|
id: generateId(),
|
|
requesterId: userId,
|
|
resourceType: AccessRequestType.Stream,
|
|
resourceId: streamId
|
|
})
|
|
|
|
await AccessRequestsEmitter.emit(AccessRequestsEmitter.events.Created, {
|
|
request: req
|
|
})
|
|
|
|
return buildStreamAccessRequestGraphQLReturn(req)
|
|
}
|
|
|
|
/**
|
|
* Get pending stream access requests
|
|
*/
|
|
export async function getPendingStreamRequests(
|
|
streamId: string
|
|
): Promise<StreamAccessRequestGraphQLReturn[]> {
|
|
const reqs = await getPendingAccessRequests(AccessRequestType.Stream, streamId)
|
|
return reqs.map(buildStreamAccessRequestGraphQLReturn)
|
|
}
|
|
|
|
/**
|
|
* Accept or decline a pending access request
|
|
*/
|
|
export async function processPendingStreamRequest(
|
|
userId: string,
|
|
requestId: string,
|
|
accept: boolean,
|
|
role: StreamRoles = Roles.Stream.Contributor,
|
|
resourceAccessRules: MaybeNullOrUndefined<TokenResourceIdentifier[]>
|
|
) {
|
|
const req = await getPendingAccessRequest(requestId, AccessRequestType.Stream)
|
|
if (!req) {
|
|
throw new AccessRequestProcessingError('No request with this ID exists')
|
|
}
|
|
|
|
try {
|
|
await validateStreamAccess(
|
|
userId,
|
|
req.resourceId,
|
|
Roles.Stream.Owner,
|
|
resourceAccessRules
|
|
)
|
|
} catch (e: unknown) {
|
|
const err = ensureError(e, 'Stream access validation failed')
|
|
if (err instanceof StreamInvalidAccessError) {
|
|
throw new AccessRequestProcessingError(
|
|
'You must own the stream to process access requests',
|
|
{ cause: err }
|
|
)
|
|
} else {
|
|
throw err
|
|
}
|
|
}
|
|
|
|
if (accept) {
|
|
await addOrUpdateStreamCollaborator(
|
|
req.resourceId,
|
|
req.requesterId,
|
|
role,
|
|
userId,
|
|
resourceAccessRules
|
|
)
|
|
}
|
|
|
|
await deleteRequestById(req.id)
|
|
|
|
await AccessRequestsEmitter.emit(AccessRequestsEmitter.events.Finalized, {
|
|
request: req,
|
|
approved: accept ? { role } : undefined,
|
|
finalizedBy: userId
|
|
})
|
|
}
|