Files
outline/server/commands/accountProvisioner.ts
Tom Moor 15b1069bcc chore: Move to Typescript (#2783)
This PR moves the entire project to Typescript. Due to the ~1000 ignores this will lead to a messy codebase for a while, but the churn is worth it – all of those ignore comments are places that were never type-safe previously.

closes #1282
2021-11-29 06:40:55 -08:00

147 lines
3.6 KiB
TypeScript

import invariant from "invariant";
import Sequelize from "sequelize";
import { Collection, Team, User } from "@server/models";
import {
AuthenticationError,
EmailAuthenticationRequiredError,
AuthenticationProviderDisabledError,
} from "../errors";
import mailer from "../mailer";
import teamCreator from "./teamCreator";
import userCreator from "./userCreator";
type Props = {
ip: string;
user: {
name: string;
email: string;
avatarUrl?: string;
username?: string;
};
team: {
name: string;
domain?: string;
subdomain: string;
avatarUrl?: string;
};
authenticationProvider: {
name: string;
providerId: string;
};
authentication: {
providerId: string;
scopes: string[];
accessToken?: string;
refreshToken?: string;
};
};
export type AccountProvisionerResult = {
// @ts-expect-error ts-migrate(2749) FIXME: 'User' refers to a value, but is being used as a t... Remove this comment to see the full error message
user: User;
// @ts-expect-error ts-migrate(2749) FIXME: 'Team' refers to a value, but is being used as a t... Remove this comment to see the full error message
team: Team;
isNewTeam: boolean;
isNewUser: boolean;
};
export default async function accountProvisioner({
ip,
user: userParams,
team: teamParams,
authenticationProvider: authenticationProviderParams,
authentication: authenticationParams,
}: Props): Promise<AccountProvisionerResult> {
let result;
try {
result = await teamCreator({
name: teamParams.name,
domain: teamParams.domain,
subdomain: teamParams.subdomain,
avatarUrl: teamParams.avatarUrl,
authenticationProvider: authenticationProviderParams,
});
} catch (err) {
throw AuthenticationError(err.message);
}
invariant(result, "Team creator result must exist");
const { authenticationProvider, team, isNewTeam } = result;
if (!authenticationProvider.enabled) {
throw AuthenticationProviderDisabledError();
}
try {
const result = await userCreator({
name: userParams.name,
email: userParams.email,
username: userParams.username,
isAdmin: isNewTeam || undefined,
avatarUrl: userParams.avatarUrl,
teamId: team.id,
ip,
authentication: {
...authenticationParams,
authenticationProviderId: authenticationProvider.id,
},
});
const { isNewUser, user } = result;
if (isNewUser) {
await mailer.sendTemplate("welcome", {
to: user.email,
teamUrl: team.url,
});
}
if (isNewUser || isNewTeam) {
let provision = isNewTeam;
// accounts for the case where a team is provisioned, but the user creation
// failed. In this case we have a valid previously created team but no
// onboarding collection.
if (!isNewTeam) {
const count = await Collection.count({
where: {
teamId: team.id,
},
});
provision = count === 0;
}
if (provision) {
await team.provisionFirstCollection(user.id);
}
}
return {
user,
team,
isNewUser,
isNewTeam,
};
} catch (err) {
if (err instanceof Sequelize.UniqueConstraintError) {
const exists = await User.findOne({
where: {
email: userParams.email,
teamId: team.id,
},
});
if (exists) {
throw EmailAuthenticationRequiredError(
"Email authentication required",
team.url
);
} else {
throw AuthenticationError(err.message, team.url);
}
}
throw err;
}
}