Skip to content

Commit 36d9e08

Browse files
committed
wip5
1 parent 4e80195 commit 36d9e08

6 files changed

Lines changed: 46 additions & 15 deletions

File tree

apps/webapp/app/components/BlankStatePanels.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ export function BranchesNoBranches({
549549
New branch
550550
</Button>
551551
}
552-
parentEnvironment={parentEnvironment}
552+
env="preview"
553553
/>
554554
}
555555
>

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import Redis, { type RedisOptions } from "ioredis";
22
import { defaultReconnectOnError } from "@internal/redis";
33
import { env } from "~/env.server";
4+
import { subDays } from "date-fns";
45

5-
const PRESENCE_KEY_PREFIX = "dev-presence:connection:";
6+
const DEV_RECENT_DEBOUNCE_SEC = 60;
7+
const DEV_RECENT_TTL = 7 * 24 * 60 * 60; // 7 days
68

79
export class DevPresence {
810
private redis: Redis;
@@ -17,13 +19,35 @@ export class DevPresence {
1719
return !!presenceValue;
1820
}
1921

20-
async setConnected(environmentId: string, ttl: number) {
22+
async setConnected({ userId, projectId, environmentId, ttl }: { userId: string; projectId: string; environmentId: string; ttl: number; }) {
2123
const presenceKey = this.getPresenceKey(environmentId);
2224
await this.redis.setex(presenceKey, ttl, new Date().toISOString());
25+
26+
const touchKey = this.getTouchKey(environmentId);
27+
const acquired = await this.redis.set(touchKey, "1", "EX", DEV_RECENT_DEBOUNCE_SEC, "NX");
28+
29+
if (acquired !== null) {
30+
const recentKey = this.getRecentKey(userId, projectId);
31+
const now = new Date();
32+
const threeDaysAgo = subDays(now, 3);
33+
await this.redis.zadd(recentKey, now.getTime(), environmentId);
34+
await this.redis.zremrangebyscore(recentKey, 0, threeDaysAgo.getTime());
35+
await this.redis.zremrangebyrank(recentKey, 0, -51);
36+
await this.redis.expire(recentKey, DEV_RECENT_TTL);
37+
}
38+
2339
}
2440

2541
private getPresenceKey(environmentId: string) {
26-
return `${PRESENCE_KEY_PREFIX}${environmentId}`;
42+
return `dev-presence:connection:${environmentId}`;
43+
}
44+
45+
private getRecentKey(userId: string, projectId: string) {
46+
return `dev-recent:${userId}:${projectId}`;
47+
}
48+
49+
private getTouchKey(environmentId: string) {
50+
return `dev-recent-touch:${environmentId}`;
2751
}
2852
}
2953

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export default function Page() {
320320
New branch…
321321
</Button>
322322
}
323-
parentEnvironment={branchableEnvironment}
323+
env="preview"
324324
/>
325325
)}
326326
</PageAccessories>
@@ -916,17 +916,17 @@ function updateBranchState({
916916

917917
export function NewBranchPanel({
918918
button,
919-
parentEnvironment,
919+
env,
920920
}: {
921921
button: React.ReactNode;
922-
parentEnvironment: { id: string };
922+
env: "preview" | "development";
923923
}) {
924924
const lastSubmission = useActionData<typeof action>();
925925
const location = useLocation();
926926
const [searchParams, setSearchParams] = useSearchParams();
927927
const [isOpen, setIsOpen] = useState(false);
928928

929-
const [form, { parentEnvironmentId, branchName, failurePath }] = useForm({
929+
const [form, { env: envField, branchName, failurePath }] = useForm({
930930
id: "create-branch",
931931
lastSubmission: lastSubmission as any,
932932
onValidate({ formData }) {
@@ -954,8 +954,8 @@ export function NewBranchPanel({
954954
<Form method="post" {...form.props} className="w-full">
955955
<Fieldset className="max-w-full gap-y-3">
956956
<input
957-
value={parentEnvironment.id}
958-
{...conform.input(parentEnvironmentId, { type: "hidden" })}
957+
value={env}
958+
{...conform.input(envField, { type: "hidden" })}
959959
/>
960960
<input
961961
value={location.pathname}

apps/webapp/app/routes/engine.v1.dev.presence.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { json } from "@remix-run/server-runtime";
2+
import invariant from "tiny-invariant";
23
import { env } from "~/env.server";
34
import { devPresence } from "~/presenters/v3/DevPresence.server";
45
import { authenticateApiRequestWithFailure } from "~/services/apiAuth.server";
@@ -12,11 +13,17 @@ export const loader = createSSELoader({
1213
handler: async ({ id, controller, debug, request }) => {
1314
const authentication = await authenticateApiRequestWithFailure(request);
1415

16+
1517
if (!authentication.ok) {
1618
throw json({ error: "Invalid or Missing API key" }, { status: 401 });
1719
}
1820

1921
const environmentId = authentication.environment.id;
22+
const projectId = authentication.environment.projectId;
23+
const userId = authentication.environment.orgMember?.userId;
24+
25+
invariant(userId, "No userId on dev environment");
26+
2027
const ttl = env.DEV_PRESENCE_TTL_MS / 1000;
2128

2229
return {
@@ -27,11 +34,11 @@ export const loader = createSSELoader({
2734
},
2835
initStream: async ({ send }) => {
2936
// Set initial presence with more context
30-
await devPresence.setConnected(environmentId, ttl);
37+
await devPresence.setConnected({ userId, projectId, environmentId, ttl });
3138
send({ event: "start", data: `Started ${id}` });
3239
},
3340
iterator: async ({ send, date }) => {
34-
await devPresence.setConnected(environmentId, ttl);
41+
await devPresence.setConnected({ userId, projectId, environmentId, ttl });
3542
send({ event: "time", data: new Date().toISOString() });
3643
},
3744
cleanup: async () => {},

packages/cli-v3/src/commands/deploy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ import { getProjectClient, upsertBranch } from "../utilities/session.js";
5757
import { getTmpDir } from "../utilities/tempDirectories.js";
5858
import { spinner } from "../utilities/windows.js";
5959
import { login } from "./login.js";
60-
import { archivePreviewOrDevBranch } from "./preview.js";
6160
import { updateTriggerPackages } from "./update.js";
61+
import { archivePreviewBranch } from "./preview.js";
6262

6363
const DeployCommandOptions = CommonCommandOptions.extend({
6464
dryRun: z.boolean().default(false),
@@ -323,7 +323,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
323323
log.message(`Pull request ${gitMeta?.pullRequestNumber} is ${gitMeta?.pullRequestState}.`);
324324
const $buildSpinner = spinner();
325325
$buildSpinner.start(`Archiving preview branch: "${branch}"`);
326-
const result = await archivePreviewOrDevBranch(authorization, branch, resolvedConfig.project);
326+
const result = await archivePreviewBranch(authorization, branch, resolvedConfig.project);
327327
$buildSpinner.stop(
328328
result ? `Successfully archived "${branch}"` : `Failed to archive "${branch}".`
329329
);

packages/cli-v3/src/commands/preview.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ async function _previewArchiveCommand(dir: string, options: PreviewCommandOption
128128
return result;
129129
}
130130

131-
async function archivePreviewBranch(
131+
export async function archivePreviewBranch(
132132
authorization: LoginResultOk,
133133
branch: string,
134134
project: string

0 commit comments

Comments
 (0)