HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/agentcloud/webapp/src/controllers/team.ts
'use strict';

import { dynamicResponse } from '@dr';
import Permission from '@permission';
import getAirbyteApi, { AirbyteApiType } from 'airbyte/api';
import bcrypt from 'bcrypt';
import { addTeam, addTeamMember, getTeamById, getTeamWithMembers, removeTeamMember, setMemberPermissions } from 'db/team';
import createAccount from 'lib/account/create';
import { calcPerms } from 'lib/middleware/auth/setpermissions';
import toObjectId from 'misc/toobjectid';
import { Binary, ObjectId } from 'mongodb';
import { TEAM_BITS } from 'permissions/bits';
import Permissions from 'permissions/permissions';
import Roles from 'permissions/roles';

import { Account, addAccount, changeAccountPassword, getAccountByEmail,
	getAccountById, 	getAccountTeamMember, OAuthRecordType, pushAccountOrg,
	pushAccountTeam, setCurrentTeam, verifyAccount } from '../db/account';
import { addOrg, getOrgById } from '../db/org';
import { addVerification, getAndDeleteVerification,VerificationTypes } from '../db/verification';
import * as ses from '../lib/email/ses';

export async function teamData(req, res, _next) {
	const [team] = await Promise.all([
		getTeamWithMembers(req.params.resourceSlug),
	]);
	return {
		team,
		csrf: req.csrfToken(),
	};
};

/**
 * GET /[resourceSlug]/team
 * team/invites page html
 */
export async function teamPage(app, req, res, next) {
	const data = await teamData(req, res, next);
	res.locals.data = { ...data, account: res.locals.account };
	return app.render(req, res, `/${req.params.resourceSlug}/team`);
}

/**
 * GET /team.json
 * team/invites page json data
 */
export async function teamJson(req, res, next) {
	const data = await teamData(req, res, next);
	return res.json({ ...data, account: res.locals.account });
}

/**
 * @api {post} /forms/team/invite
 * @apiName invite
 * @apiGroup Team
 *
 * @apiParam {String} email Email of person to invite
 */
export async function inviteTeamMemberApi(req, res) {
	const { name, email } = req.body;
	if (!email || typeof email !== 'string' || email.length === 0) {
		return dynamicResponse(req, res, 403, { error: 'Invalid inputs' });
	}
	let foundAccount = await getAccountByEmail(email);
	const invitingTeam = res.locals.matchingOrg.teams
		.find(t => t.id.toString() === req.params.resourceSlug);
	if (!foundAccount) {
		const { addedAccount } = await createAccount(email, name, null, true);
		await addTeamMember(req.params.resourceSlug, addedAccount.insertedId);
		foundAccount = await getAccountByEmail(email);
	} else {
		//account with that email was found
		const foundTeam = await getTeamById(req.params.resourceSlug);
		if (foundTeam.members.some(tmid => tmid.toString() === foundAccount._id.toString())) {
			return dynamicResponse(req, res, 403, { error: 'User is already on your team' });
		}
		await addTeamMember(req.params.resourceSlug, foundAccount._id);
	}
	if (!foundAccount.orgs.find(f => f.id === res.locals.matchingOrg.id)) {
		//if user isnt in org, add the new org to their account array with the invitingTeam already pushed
		await pushAccountOrg(foundAccount._id, {
			...res.locals.matchingOrg,
			teams: [invitingTeam],
		});
	} else {
		//otherwise theyre already in the org, just push the single team to the matching org
		await pushAccountTeam(foundAccount._id, res.locals.matchingOrg.id, invitingTeam);
	}
	//member invited
	return dynamicResponse(req, res, 200, { });
}

/**
 * @api {delete} /forms/team/invite
 * @apiName invite
 * @apiGroup Team
 *
 * @apiParam {String} email Email of person to invite
 */
export async function deleteTeamMemberApi(req, res) {
	const { memberId } = req.body;
	//account with that memberId
	const memberAccount = await getAccountById(memberId);
	if (memberAccount) {	
		const foundTeam = await getTeamById(req.params.resourceSlug);
		const org = res.locals.matchingOrg;//await getOrgById(foundTeam.orgId);
		if (!org) {
			return dynamicResponse(req, res, 403, { error: 'User org not found' });
		} else {
			if (!foundTeam.members.some(m => m.equals(memberAccount._id))) {
				return dynamicResponse(req, res, 403, { error: 'Cannot remove org user' });
			}
		}
		// if (!foundTeam.members.some(m => m.equals(memberAccount._id))) {
		// 	return dynamicResponse(req, res, 403, { error: 'User not found in your team' });
		// }
		const removeRes = await removeTeamMember(req.params.resourceSlug, memberId.toString());
		if (removeRes?.modifiedCount < 1) {
			return dynamicResponse(req, res, 403, { error: 'User not found in your team' });
		}
	} else {
		return dynamicResponse(req, res, 403, { error: 'User not found' });
	}
	return dynamicResponse(req, res, 302, {  });
}

/**
 * @api {post} /forms/team/add
 * @apiName add
 * @apiGroup Team
 *
 * @apiParam {String} teamName Name of new team
 */
export async function addTeamApi(req, res) {
	const { teamName } = req.body;
	if (!teamName || typeof teamName !== 'string' || teamName.length === 0) {
		return dynamicResponse(req, res, 400, { error: 'Invalid inputs' });
	}
	const addedTeam = await addTeam({
		name: teamName,
		ownerId: toObjectId(res.locals.account._id),
		orgId: toObjectId(res.locals.matchingOrg.id),
		members: [toObjectId(res.locals.account._id)],
		dateCreated: new Date(),
		permissions: {
			[res.locals.account._id.toString()]: new Binary((new Permission(Roles.REGISTERED_USER.base64).array)),
		}
	});
	await addTeamMember(addedTeam.insertedId, res.locals.account._id);
	await pushAccountTeam(res.locals.account._id, res.locals.matchingOrg.id, {
		id: addedTeam.insertedId,
		name: teamName,
		ownerId: toObjectId(res.locals.account._id),
	});
	return dynamicResponse(req, res, 200, { _id: addedTeam.insertedId, orgId: res.locals.matchingOrg.id });
}

/**
 * @api {post} /forms/team/[memberId]/edit
 * @apiName edit
 * @apiGroup Team
 *
 * @apiParam {String} teamName Name of new team
 */
export async function editTeamMemberApi(req, res) {

	const { resourceSlug, memberId } = req.params;
	const { template } = req.body;
	
	if (memberId === res.locals.matchingTeam.ownerId.toString()) {
		return dynamicResponse(req, res, 400, { error: 'Team owner permissions can\'t be edited' });
	}

	const editingMember = await getAccountById(req.params.memberId);
	
	let updatingPermissions;
	if (template) {
		updatingPermissions = new Permission(template); //TODO: template (.base64 of official roles)
	} else {
		const updatingPermissions = new Permission(editingMember.permissions.toString('base64'));		
		updatingPermissions.handleBody(req.body, res.locals.permissions, TEAM_BITS);
		await setMemberPermissions(resourceSlug, memberId, updatingPermissions);
	}

	//For the bits that are org level, set those in the org map
	// await setOrgPermissions(resourceSlug, memberId, updatingPermissions);

	return dynamicResponse(req, res, 200, { });

}

export async function teamMemberData(req, res, _next) {
	const [teamMember] = await Promise.all([
		getAccountTeamMember(req.params.memberId, req.params.resourceSlug),
	]);
	teamMember.permissions = calcPerms(teamMember, res.locals.matchingOrg, res.locals.matchingTeam).base64;
	return {
		teamMember,
		csrf: req.csrfToken(),
	};
};

/**
 * GET /[resourceSlug]/team/[memberId].json
 * team member json data
 */
export async function teamMemberJson(req, res, next) {
	const data = await teamMemberData(req, res, next);
	return res.json({ ...data, account: res.locals.account });
}

/**
 * GET /[resourceSlug]/team/[memberId]/edit
 * edit team member page html
 */
export async function memberEditPage(app, req, res, next) {
	const data = await teamMemberData(req, res, next);
	res.locals.data = { ...data, account: res.locals.account };
	return app.render(req, res, `/${req.params.resourceSlug}/team/${req.params.memberId}/edit`);
}