7b862b9438
* Delete the proxy app (was v2) * Delete RunPresenterElectric * Select the best proj/org/env * Storing current proj/env in DB. Initial selection logic working with tasks page * 2sm needed to be in the Tailwind merge list * Move the task stream route (although we don’t actually use the env for now) * Alerts moved from /v3 * API keys page moved from /v3 * Concurrency page moved from /v3 * WIP on side menu sections * Improved the accordion animation * Moved schedules from /v3 * More pages moved * Move pages working * Run page working * Schedules working * Moved deployments * Alert pages moved * Delete electric hooks, not used * Started setting up blank states * Test page working * Removed “Select task” from the test page * Some work on deployment page * Style tweaks * Redirect from project root to approriate env * Improved env selector styling * Fix for jsx errors * Better min width on env selector * Improved the env switching logic * Added deployments to env routing * Redirect deployments to the correct env * Redirect run from proj to env * JSX icon fix * Only allow single env schedules from now on * Remove env var count from the API keys page * Move improvements and redirects * Project settings moved * Fix for scroll area on test page * Tweaked the test design * Made recent payloads column narrower * Improved the test layout some more * Added org icon, new project selector menu * WIP on org switching menu * Org switching is working * New menu working well, removed old side menu items * Buttons can now have a component name or an actual component for their icons * Removed the Projects page, instead redirect appropriately * Fix for broken blank states * Minor run table improvements * Removed unused switcher log and logic * Concurrency page fix for invalid html, improved layout * Minor improvements * Moved the side menu to the project level * Improved account styling * Moved org settings pages (with redirects) * Add current plan to billing side menu link * Upgrade to get staging from env dropdown * New env badge on concurrency limits page * Show Run Engine version in span presenter * New promote icon * Concurrency limits page is the sum of engine v1 + v2 queues * Fix for missing batch import * Added currentConcurrencyOfEnvQueue function * Basic avatar setting working * Avatar setting is working * You can change the color of your icon * Avatar improvements * Bugfix for mising prop * Removed some old env badges * Fixed replaying * Removed EnvironmentLabel * Old env badge deleted, changed everywhere to the new one * Fix for Slack integration paths * Fix for waitpoint completion form moving * Bulk replay/cancel env fix * Fix for alert webhook path * Redirect projects/v3/* to project/* * Fixes for CLI redirect routes * Remove welcome email (unused) * Change how we count schedules towards your limits * Use new schedules limits when checking a schedule * Added projectId back in to task queries (indexes) * WIP dev presence * CLI modal * Moved things around and use Context * Fix for p inside p * Dev connected status on run page * Correct dev env (not a teammates) * Show disconnected message at the end * Minor tweak on project dropdown icon padding * Fix for inconsistent date format for presence * Added a message when pushing to the billing page * Center the team page * Project settings page centered * Improvements to the dev presence
216 lines
4.9 KiB
TypeScript
216 lines
4.9 KiB
TypeScript
import type { Prisma, User } from "@trigger.dev/database";
|
|
import type { GitHubProfile } from "remix-auth-github";
|
|
import { prisma } from "~/db.server";
|
|
import { env } from "~/env.server";
|
|
import {
|
|
DashboardPreferences,
|
|
getDashboardPreferences,
|
|
} from "~/services/dashboardPreferences.server";
|
|
export type { User } from "@trigger.dev/database";
|
|
|
|
type FindOrCreateMagicLink = {
|
|
authenticationMethod: "MAGIC_LINK";
|
|
email: string;
|
|
};
|
|
|
|
type FindOrCreateGithub = {
|
|
authenticationMethod: "GITHUB";
|
|
email: User["email"];
|
|
authenticationProfile: GitHubProfile;
|
|
authenticationExtraParams: Record<string, unknown>;
|
|
};
|
|
|
|
type FindOrCreateUser = FindOrCreateMagicLink | FindOrCreateGithub;
|
|
|
|
type LoggedInUser = {
|
|
user: User;
|
|
isNewUser: boolean;
|
|
};
|
|
|
|
export async function findOrCreateUser(input: FindOrCreateUser): Promise<LoggedInUser> {
|
|
switch (input.authenticationMethod) {
|
|
case "GITHUB": {
|
|
return findOrCreateGithubUser(input);
|
|
}
|
|
case "MAGIC_LINK": {
|
|
return findOrCreateMagicLinkUser(input);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function findOrCreateMagicLinkUser(
|
|
input: FindOrCreateMagicLink
|
|
): Promise<LoggedInUser> {
|
|
if (env.WHITELISTED_EMAILS && !new RegExp(env.WHITELISTED_EMAILS).test(input.email)) {
|
|
throw new Error("This email is unauthorized");
|
|
}
|
|
|
|
const existingUser = await prisma.user.findFirst({
|
|
where: {
|
|
email: input.email,
|
|
},
|
|
});
|
|
|
|
const adminEmailRegex = env.ADMIN_EMAILS ? new RegExp(env.ADMIN_EMAILS) : undefined;
|
|
const makeAdmin = adminEmailRegex ? adminEmailRegex.test(input.email) : false;
|
|
|
|
const user = await prisma.user.upsert({
|
|
where: {
|
|
email: input.email,
|
|
},
|
|
update: {
|
|
email: input.email,
|
|
},
|
|
create: {
|
|
email: input.email,
|
|
authenticationMethod: "MAGIC_LINK",
|
|
admin: makeAdmin, // only on create, to prevent automatically removing existing admins
|
|
},
|
|
});
|
|
|
|
return {
|
|
user,
|
|
isNewUser: !existingUser,
|
|
};
|
|
}
|
|
|
|
export async function findOrCreateGithubUser({
|
|
email,
|
|
authenticationProfile,
|
|
authenticationExtraParams,
|
|
}: FindOrCreateGithub): Promise<LoggedInUser> {
|
|
const name = authenticationProfile._json.name;
|
|
let avatarUrl: string | undefined = undefined;
|
|
if (authenticationProfile.photos[0]) {
|
|
avatarUrl = authenticationProfile.photos[0].value;
|
|
}
|
|
const displayName = authenticationProfile.displayName;
|
|
const authProfile = authenticationProfile
|
|
? (authenticationProfile as unknown as Prisma.JsonObject)
|
|
: undefined;
|
|
const authExtraParams = authenticationExtraParams
|
|
? (authenticationExtraParams as unknown as Prisma.JsonObject)
|
|
: undefined;
|
|
|
|
const authIdentifier = `github:${authenticationProfile.id}`;
|
|
|
|
const existingUser = await prisma.user.findUnique({
|
|
where: {
|
|
authIdentifier,
|
|
},
|
|
});
|
|
|
|
const existingEmailUser = await prisma.user.findUnique({
|
|
where: {
|
|
email,
|
|
},
|
|
});
|
|
|
|
if (existingEmailUser && !existingUser) {
|
|
const user = await prisma.user.update({
|
|
where: {
|
|
email,
|
|
},
|
|
data: {
|
|
authenticationProfile: authProfile,
|
|
authenticationExtraParams: authExtraParams,
|
|
avatarUrl,
|
|
authIdentifier,
|
|
},
|
|
});
|
|
|
|
return {
|
|
user,
|
|
isNewUser: false,
|
|
};
|
|
}
|
|
|
|
if (existingEmailUser && existingUser) {
|
|
const user = await prisma.user.update({
|
|
where: {
|
|
id: existingUser.id,
|
|
},
|
|
data: {},
|
|
});
|
|
|
|
return {
|
|
user,
|
|
isNewUser: false,
|
|
};
|
|
}
|
|
|
|
const user = await prisma.user.upsert({
|
|
where: {
|
|
authIdentifier,
|
|
},
|
|
update: {},
|
|
create: {
|
|
authenticationProfile: authProfile,
|
|
authenticationExtraParams: authExtraParams,
|
|
name,
|
|
avatarUrl,
|
|
displayName,
|
|
authIdentifier,
|
|
email,
|
|
authenticationMethod: "GITHUB",
|
|
},
|
|
});
|
|
|
|
return {
|
|
user,
|
|
isNewUser: !existingUser,
|
|
};
|
|
}
|
|
|
|
export type UserWithDashboardPreferences = User & {
|
|
dashboardPreferences: DashboardPreferences;
|
|
};
|
|
|
|
export async function getUserById(id: User["id"]) {
|
|
const user = await prisma.user.findUnique({ where: { id } });
|
|
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
|
|
const dashboardPreferences = getDashboardPreferences(user.dashboardPreferences);
|
|
|
|
return {
|
|
...user,
|
|
dashboardPreferences,
|
|
};
|
|
}
|
|
|
|
export async function getUserByEmail(email: User["email"]) {
|
|
return prisma.user.findUnique({ where: { email } });
|
|
}
|
|
|
|
export function updateUser({
|
|
id,
|
|
name,
|
|
email,
|
|
marketingEmails,
|
|
referralSource,
|
|
}: Pick<User, "id" | "name" | "email"> & {
|
|
marketingEmails?: boolean;
|
|
referralSource?: string;
|
|
}) {
|
|
return prisma.user.update({
|
|
where: { id },
|
|
data: { name, email, marketingEmails, referralSource, confirmedBasicDetails: true },
|
|
});
|
|
}
|
|
|
|
export async function grantUserCloudAccess({ id, inviteCode }: { id: string; inviteCode: string }) {
|
|
return prisma.user.update({
|
|
where: { id },
|
|
data: {
|
|
invitationCode: {
|
|
connect: {
|
|
code: inviteCode,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|