Skip to content

Commit b803efb

Browse files
committed
Merge remote-tracking branch 'origin/main' into v4/self-hosting
2 parents 918542c + 1bb1fc4 commit b803efb

File tree

16 files changed

+170
-38
lines changed

16 files changed

+170
-38
lines changed

.changeset/gold-insects-invite.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"trigger.dev": patch
3+
"@trigger.dev/core": patch
4+
---
5+
6+
Expose esbuild `keepNames` option (experimental)

.changeset/wild-mirrors-return.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"trigger.dev": patch
3+
"@trigger.dev/core": patch
4+
---
5+
6+
Expose esbuild `minify` option (experimental)

apps/webapp/app/env.server.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { z } from "zod";
22
import { SecretStoreOptionsSchema } from "./services/secrets/secretStoreOptionsSchema.server";
33
import { isValidDatabaseUrl } from "./utils/db";
44
import { isValidRegex } from "./utils/regex";
5+
import { BoolEnv } from "./utils/boolEnv";
56

67
const EnvironmentSchema = z.object({
78
NODE_ENV: z.union([z.literal("development"), z.literal("production"), z.literal("test")]),
@@ -50,7 +51,7 @@ const EnvironmentSchema = z.object({
5051
RESEND_API_KEY: z.string().optional(),
5152
SMTP_HOST: z.string().optional(),
5253
SMTP_PORT: z.coerce.number().optional(),
53-
SMTP_SECURE: z.coerce.boolean().optional(),
54+
SMTP_SECURE: BoolEnv.optional(),
5455
SMTP_USER: z.string().optional(),
5556
SMTP_PASSWORD: z.string().optional(),
5657

@@ -337,7 +338,7 @@ const EnvironmentSchema = z.object({
337338
ALERT_RESEND_API_KEY: z.string().optional(),
338339
ALERT_SMTP_HOST: z.string().optional(),
339340
ALERT_SMTP_PORT: z.coerce.number().optional(),
340-
ALERT_SMTP_SECURE: z.coerce.boolean().optional(),
341+
ALERT_SMTP_SECURE: BoolEnv.optional(),
341342
ALERT_SMTP_USER: z.string().optional(),
342343
ALERT_SMTP_PASSWORD: z.string().optional(),
343344
ALERT_RATE_LIMITER_EMISSION_INTERVAL: z.coerce.number().int().default(2_500),
@@ -377,7 +378,7 @@ const EnvironmentSchema = z.object({
377378
MAX_SEQUENTIAL_INDEX_FAILURE_COUNT: z.coerce.number().default(96),
378379

379380
LOOPS_API_KEY: z.string().optional(),
380-
MARQS_DISABLE_REBALANCING: z.coerce.boolean().default(false),
381+
MARQS_DISABLE_REBALANCING: BoolEnv.default(false),
381382
MARQS_VISIBILITY_TIMEOUT_MS: z.coerce
382383
.number()
383384
.int()
@@ -455,7 +456,7 @@ const EnvironmentSchema = z.object({
455456
.number()
456457
.int()
457458
.default(60_000 * 10),
458-
RUN_ENGINE_DEBUG_WORKER_NOTIFICATIONS: z.coerce.boolean().default(false),
459+
RUN_ENGINE_DEBUG_WORKER_NOTIFICATIONS: BoolEnv.default(false),
459460
RUN_ENGINE_PARENT_QUEUE_LIMIT: z.coerce.number().int().default(1000),
460461
RUN_ENGINE_CONCURRENCY_LIMIT_BIAS: z.coerce.number().default(0.75),
461462
RUN_ENGINE_AVAILABLE_CAPACITY_BIAS: z.coerce.number().default(0.3),

apps/webapp/app/models/user.server.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
getDashboardPreferences,
88
} from "~/services/dashboardPreferences.server";
99
export type { User } from "@trigger.dev/database";
10-
10+
import { assertEmailAllowed } from "~/utils/email";
1111
type FindOrCreateMagicLink = {
1212
authenticationMethod: "MAGIC_LINK";
1313
email: string;
@@ -38,31 +38,29 @@ export async function findOrCreateUser(input: FindOrCreateUser): Promise<LoggedI
3838
}
3939
}
4040

41-
export async function findOrCreateMagicLinkUser(
42-
input: FindOrCreateMagicLink
43-
): Promise<LoggedInUser> {
44-
if (env.WHITELISTED_EMAILS && !new RegExp(env.WHITELISTED_EMAILS).test(input.email)) {
45-
throw new Error("This email is unauthorized");
46-
}
41+
export async function findOrCreateMagicLinkUser({
42+
email,
43+
}: FindOrCreateMagicLink): Promise<LoggedInUser> {
44+
assertEmailAllowed(email);
4745

4846
const existingUser = await prisma.user.findFirst({
4947
where: {
50-
email: input.email,
48+
email,
5149
},
5250
});
5351

5452
const adminEmailRegex = env.ADMIN_EMAILS ? new RegExp(env.ADMIN_EMAILS) : undefined;
55-
const makeAdmin = adminEmailRegex ? adminEmailRegex.test(input.email) : false;
53+
const makeAdmin = adminEmailRegex ? adminEmailRegex.test(email) : false;
5654

5755
const user = await prisma.user.upsert({
5856
where: {
59-
email: input.email,
57+
email,
6058
},
6159
update: {
62-
email: input.email,
60+
email,
6361
},
6462
create: {
65-
email: input.email,
63+
email,
6664
authenticationMethod: "MAGIC_LINK",
6765
admin: makeAdmin, // only on create, to prevent automatically removing existing admins
6866
},
@@ -79,6 +77,8 @@ export async function findOrCreateGithubUser({
7977
authenticationProfile,
8078
authenticationExtraParams,
8179
}: FindOrCreateGithub): Promise<LoggedInUser> {
80+
assertEmailAllowed(email);
81+
8282
const name = authenticationProfile._json.name;
8383
let avatarUrl: string | undefined = undefined;
8484
if (authenticationProfile.photos[0]) {

apps/webapp/app/presenters/v3/RunPresenter.server.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ export class RunPresenter {
3232
environmentSlug,
3333
runFriendlyId,
3434
showDeletedLogs,
35+
showDebug,
3536
}: {
3637
userId: string;
3738
projectSlug: string;
3839
organizationSlug: string;
3940
environmentSlug: string;
4041
runFriendlyId: string;
4142
showDeletedLogs: boolean;
43+
showDebug: boolean;
4244
}) {
4345
const run = await this.#prismaClient.taskRun.findFirstOrThrow({
4446
select: {
@@ -131,7 +133,8 @@ export class RunPresenter {
131133
getTaskEventStoreTableForRun(run),
132134
run.traceId,
133135
run.rootTaskRun?.createdAt ?? run.createdAt,
134-
run.completedAt ?? undefined
136+
run.completedAt ?? undefined,
137+
{ includeDebugLogs: showDebug }
135138
);
136139
if (!traceSummary) {
137140
return {

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import {
9595
} from "~/utils/pathBuilder";
9696
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
9797
import { SpanView } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route";
98+
import { useSearchParams } from "~/hooks/useSearchParam";
9899

99100
const resizableSettings = {
100101
parent: {
@@ -133,6 +134,9 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
133134
const impersonationId = await getImpersonationId(request);
134135
const { projectParam, organizationSlug, envParam, runParam } = v3RunParamsSchema.parse(params);
135136

137+
const url = new URL(request.url);
138+
const showDebug = url.searchParams.get("showDebug") === "true";
139+
136140
const presenter = new RunPresenter();
137141
const [error, result] = await tryCatch(
138142
presenter.call({
@@ -142,6 +146,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
142146
projectSlug: projectParam,
143147
runFriendlyId: runParam,
144148
environmentSlug: envParam,
149+
showDebug,
145150
})
146151
);
147152

@@ -505,15 +510,18 @@ function TasksTreeView({
505510
const isAdmin = useHasAdminAccess();
506511
const [filterText, setFilterText] = useState("");
507512
const [errorsOnly, setErrorsOnly] = useState(false);
508-
const [showDebug, setShowDebug] = useState(false);
509513
const [showDurations, setShowDurations] = useState(true);
510514
const [showQueueTime, setShowQueueTime] = useState(false);
511515
const [scale, setScale] = useState(0);
512516
const parentRef = useRef<HTMLDivElement>(null);
513517
const treeScrollRef = useRef<HTMLDivElement>(null);
514518
const timelineScrollRef = useRef<HTMLDivElement>(null);
519+
const { value, replace } = useSearchParams();
520+
521+
const searchValue = value("showDebug");
522+
const showDebug = searchValue !== undefined ? searchValue === "true" : false;
515523

516-
const displayEvents = showDebug ? events : events.filter((event) => !event.data.isDebug);
524+
const displayEvents = events;
517525
const queuedTime = showQueueTime ? undefined : queuedDuration;
518526

519527
const {
@@ -560,7 +568,11 @@ function TasksTreeView({
560568
label="Debug"
561569
shortcut={{ modifiers: ["shift"], key: "D" }}
562570
checked={showDebug}
563-
onCheckedChange={(e) => setShowDebug(e.valueOf())}
571+
onCheckedChange={(checked) => {
572+
replace({
573+
showDebug: checked ? "true" : "false",
574+
});
575+
}}
564576
/>
565577
)}
566578
<Switch

apps/webapp/app/routes/login._index/route.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson";
66
import { LoginPageLayout } from "~/components/LoginPageLayout";
77
import { Button, LinkButton } from "~/components/primitives/Buttons";
88
import { Fieldset } from "~/components/primitives/Fieldset";
9+
import { FormError } from "~/components/primitives/FormError";
910
import { Header1 } from "~/components/primitives/Headers";
1011
import { Paragraph } from "~/components/primitives/Paragraph";
1112
import { TextLink } from "~/components/primitives/TextLink";
1213
import { isGithubAuthSupported } from "~/services/auth.server";
1314
import { commitSession, setRedirectTo } from "~/services/redirectTo.server";
1415
import { getUserId } from "~/services/session.server";
16+
import { getUserSession } from "~/services/sessionStorage.server";
1517
import { requestUrl } from "~/utils/requestUrl.server";
1618

1719
export const meta: MetaFunction = ({ matches }) => {
@@ -48,17 +50,34 @@ export async function loader({ request }: LoaderFunctionArgs) {
4850
const session = await setRedirectTo(request, redirectTo);
4951

5052
return typedjson(
51-
{ redirectTo, showGithubAuth: isGithubAuthSupported },
53+
{
54+
redirectTo,
55+
showGithubAuth: isGithubAuthSupported,
56+
authError: null,
57+
},
5258
{
5359
headers: {
5460
"Set-Cookie": await commitSession(session),
5561
},
5662
}
5763
);
5864
} else {
65+
const session = await getUserSession(request);
66+
const error = session.get("auth:error");
67+
68+
let authError: string | undefined;
69+
if (error) {
70+
if ("message" in error) {
71+
authError = error.message;
72+
} else {
73+
authError = JSON.stringify(error, null, 2);
74+
}
75+
}
76+
5977
return typedjson({
6078
redirectTo: null,
6179
showGithubAuth: isGithubAuthSupported,
80+
authError,
6281
});
6382
}
6483
}
@@ -81,7 +100,7 @@ export default function LoginPage() {
81100
Create an account or login
82101
</Paragraph>
83102
<Fieldset className="w-full">
84-
<div className="flex flex-col gap-y-2">
103+
<div className="flex flex-col items-center gap-y-2">
85104
{data.showGithubAuth && (
86105
<Button
87106
type="submit"
@@ -103,6 +122,7 @@ export default function LoginPage() {
103122
<EnvelopeIcon className="mr-2 size-5 text-text-bright" />
104123
Continue with Email
105124
</LinkButton>
125+
{data.authError && <FormError>{data.authError}</FormError>}
106126
</div>
107127
<Paragraph variant="extra-small" className="mt-2 text-center">
108128
By signing up you agree to our{" "}

apps/webapp/app/services/email.server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { EmailClient, MailTransportOptions } from "emails";
33
import type { SendEmailOptions } from "remix-auth-email-link";
44
import { redirect } from "remix-typedjson";
55
import { env } from "~/env.server";
6-
import type { User } from "~/models/user.server";
76
import type { AuthUser } from "./authUser";
87
import { workerQueue } from "./worker.server";
98
import { logger } from "./logger.server";
109
import { singleton } from "~/utils/singleton";
10+
import { assertEmailAllowed } from "~/utils/email";
1111

1212
const client = singleton(
1313
"email-client",
@@ -66,6 +66,8 @@ function buildTransportOptions(alerts?: boolean): MailTransportOptions {
6666
}
6767

6868
export async function sendMagicLinkEmail(options: SendEmailOptions<AuthUser>): Promise<void> {
69+
assertEmailAllowed(options.emailAddress);
70+
6971
// Auto redirect when in development mode
7072
if (env.NODE_ENV === "development") {
7173
throw redirect(options.magicLink);

apps/webapp/app/utils/boolEnv.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { z } from "zod";
2+
3+
export const BoolEnv = z.preprocess((val) => {
4+
if (typeof val !== "string") {
5+
return val;
6+
}
7+
8+
return ["true", "1"].includes(val.toLowerCase().trim());
9+
}, z.boolean());

apps/webapp/app/utils/email.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { env } from "~/env.server";
2+
3+
export function assertEmailAllowed(email: string) {
4+
if (!env.WHITELISTED_EMAILS) {
5+
return;
6+
}
7+
8+
const regexp = new RegExp(env.WHITELISTED_EMAILS);
9+
10+
if (!regexp.test(email)) {
11+
throw new Error("This email is unauthorized");
12+
}
13+
}

0 commit comments

Comments
 (0)