Skip to content

Commit 3434322

Browse files
authored
chore: new Domain publish prepare steps (#4125)
## Description 1. What is this PR about (link the issue and add a short description) ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent 654c134 commit 3434322

File tree

40 files changed

+1814
-557
lines changed

40 files changed

+1814
-557
lines changed

.github/workflows/migrate.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,29 @@ jobs:
5757
env:
5858
DIRECT_URL: ${{ secrets.DIRECT_URL }}
5959

60+
# Execute db tests (@todo: can be done only after applying the migrations, now always())
61+
db-tests:
62+
if: always()
63+
64+
needs: [migrate]
65+
66+
runs-on: ubuntu-latest
67+
68+
environment:
69+
name: ${{ (startsWith(github.ref_name, 'release') && endsWith(github.ref_name, '.staging')) && 'postgres_production' || 'postgres_development' }}
70+
71+
steps:
72+
- uses: actions/checkout@v4
73+
with:
74+
ref: ${{ github.sha }} # HEAD commit instead of merge commit
75+
76+
- uses: pnpm/action-setup@v4
77+
78+
- name: pnpm instal
79+
run: pnpm -r db-test
80+
env:
81+
DIRECT_URL: ${{ secrets.DIRECT_URL }}
82+
6083
# Prints pending migrations
6184
pending:
6285
if: always()

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,6 @@ tsconfig.tsbuildinfo
6060
dist
6161

6262
# should be here otherwise if placed inside prisma-client pnpm deploy doesn't copy it
63-
packages/prisma-client/src/__generated__
63+
packages/prisma-client/src/__generated__
64+
65+
.temp

apps/builder/app/builder/features/sidebar-left/panels/pages/page-settings.stories.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ $project.set({
7171
isDeleted: false,
7272
userId: "userId",
7373
domain: "new-2x9tcd",
74+
75+
marketplaceApprovalStatus: "UNLISTED",
76+
77+
latestStaticBuild: null,
78+
previewImageAssetId: null,
7479
previewImageAsset: {
75-
id: "0",
76-
name: "0",
80+
projectId: "projectId",
81+
id: "imageId",
82+
name: "very-very-very-long-long-image-name.jpg",
7783
},
78-
marketplaceApprovalStatus: "UNLISTED",
84+
latestBuildVirtual: null,
85+
domainsVirtual: [],
7986
});
8087

8188
export const PageSettingsEdit = () => {

apps/builder/app/builder/features/topbar/domains.tsx

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,14 @@ import {
2121
import type { DomainStatus } from "@webstudio-is/prisma-client";
2222
import { CollapsibleDomainSection } from "./collapsible-domain-section";
2323
import { useCallback, useEffect, useState } from "react";
24-
import type { PublishStatus } from "@webstudio-is/prisma-client";
2524
import { formatDistance } from "date-fns/formatDistance";
2625
import { Entri } from "./entri";
2726
import { trpcClient } from "~/shared/trpc/trpc-client";
2827
import { useStore } from "@nanostores/react";
2928
import { $publisherHost } from "~/shared/nano-states";
29+
import type { Database } from "@webstudio-is/postrest/index.server";
3030

31-
export type Domain = {
32-
projectId: Project["id"];
33-
domainId: string;
34-
domain: {
35-
domain: string;
36-
error: string | null;
37-
status: DomainStatus;
38-
updatedAt: string;
39-
};
40-
txtRecord: string;
41-
cname: string;
42-
verified: boolean;
43-
latestBuid: null | {
44-
projectId: string;
45-
buildId: string;
46-
isLatestBuild: boolean;
47-
publishStatus: PublishStatus;
48-
updatedAt: string;
49-
domainId: string;
50-
};
51-
};
31+
export type Domain = Project["domainsVirtual"][number];
5232

5333
const InputEllipsis = styled(InputField, {
5434
"&>input": {
@@ -79,21 +59,58 @@ const getCname = (domain: string) => {
7959
return cnameArray.join(".");
8060
};
8161

82-
export const getStatus = (projectDomain: Domain) =>
62+
export const getStatus = (projectDomain: Project["domainsVirtual"][number]) =>
8363
projectDomain.verified
84-
? (`VERIFIED_${projectDomain.domain.status}` as const)
64+
? (`VERIFIED_${projectDomain.status}` as const)
8565
: `UNVERIFIED`;
8666

8767
export const PENDING_TIMEOUT =
8868
process.env.NODE_ENV === "production" ? 60 * 3 * 1000 : 35000;
8969

70+
export const getPublishStatusAndTextNew = ({
71+
createdAt,
72+
publishStatus,
73+
}: Pick<
74+
Database["public"]["Tables"]["latestBuildVirtual"]["Row"],
75+
"createdAt" | "publishStatus"
76+
>) => {
77+
let status = publishStatus;
78+
79+
const delta = Date.now() - new Date(createdAt).getTime();
80+
// Assume build failed after 3 minutes
81+
82+
if (publishStatus === "PENDING" && delta > PENDING_TIMEOUT) {
83+
status = "FAILED";
84+
}
85+
86+
const textStart =
87+
status === "PUBLISHED"
88+
? "Published"
89+
: status === "FAILED"
90+
? "Publish failed"
91+
: "Publishing started";
92+
93+
const statusText = `${textStart} ${formatDistance(
94+
new Date(createdAt),
95+
new Date(),
96+
{
97+
addSuffix: true,
98+
}
99+
)}`;
100+
101+
return { statusText, status };
102+
};
103+
90104
export const getPublishStatusAndText = ({
91-
updatedAt,
105+
createdAt,
92106
publishStatus,
93-
}: Pick<NonNullable<Domain["latestBuid"]>, "updatedAt" | "publishStatus">) => {
107+
}: Pick<
108+
NonNullable<Domain["latestBuildVirtual"]>,
109+
"createdAt" | "publishStatus"
110+
>) => {
94111
let status = publishStatus;
95112

96-
const delta = Date.now() - new Date(updatedAt).getTime();
113+
const delta = Date.now() - new Date(createdAt).getTime();
97114
// Assume build failed after 3 minutes
98115

99116
if (publishStatus === "PENDING" && delta > PENDING_TIMEOUT) {
@@ -108,7 +125,7 @@ export const getPublishStatusAndText = ({
108125
: "Publishing started";
109126

110127
const statusText = `${textStart} ${formatDistance(
111-
new Date(updatedAt),
128+
new Date(createdAt),
112129
new Date(),
113130
{
114131
addSuffix: true,
@@ -142,17 +159,17 @@ const getStatusText = (props: {
142159
isVerifiedActive = true;
143160
text = "Status: Active, not published";
144161

145-
if (props.projectDomain.latestBuid !== null) {
162+
if (props.projectDomain.latestBuildVirtual !== null) {
146163
const publishText = getPublishStatusAndText(
147-
props.projectDomain.latestBuid
164+
props.projectDomain.latestBuildVirtual
148165
);
149166

150167
text = publishText.statusText;
151168
isVerifiedActive = publishText.status !== "FAILED";
152169
}
153170
break;
154171
case "VERIFIED_ERROR":
155-
text = props.projectDomain.domain.error ?? text;
172+
text = props.projectDomain.error ?? text;
156173
break;
157174

158175
default:
@@ -237,16 +254,16 @@ const DomainItem = (props: {
237254
props.domainState !== "idle";
238255

239256
const status = props.projectDomain.verified
240-
? (`VERIFIED_${props.projectDomain.domain.status}` as `VERIFIED_${DomainStatus}`)
257+
? (`VERIFIED_${props.projectDomain.status}` as `VERIFIED_${DomainStatus}`)
241258
: `UNVERIFIED`;
242259

243260
const { initiallyOpen } = props;
244261

245262
const domainId = props.projectDomain.domainId;
246-
const domain = props.projectDomain.domain.domain;
247-
const domainStatus = props.projectDomain.domain.status;
248-
const domainError = props.projectDomain.domain.error;
249-
const domainUpdatedAt = props.projectDomain.domain.updatedAt;
263+
const domain = props.projectDomain.domain;
264+
const domainStatus = props.projectDomain.status;
265+
const domainError = props.projectDomain.error;
266+
const domainUpdatedAt = props.projectDomain.updatedAt;
250267

251268
const projectId = props.projectDomain.projectId;
252269
// const domain
@@ -365,7 +382,7 @@ const DomainItem = (props: {
365382
const txtRecord = {
366383
type: "TXT",
367384
host: txtEntryName,
368-
value: props.projectDomain.txtRecord,
385+
value: props.projectDomain.expectedTxtRecord,
369386
ttl: 300,
370387
} as const;
371388

@@ -589,9 +606,9 @@ export const Domains = ({
589606
<>
590607
{domains.map((projectDomain) => (
591608
<DomainItem
592-
key={projectDomain.domain.domain}
609+
key={projectDomain.domain}
593610
projectDomain={projectDomain}
594-
initiallyOpen={newDomains.has(projectDomain.domain.domain)}
611+
initiallyOpen={newDomains.has(projectDomain.domain)}
595612
refreshDomainResult={refreshDomainResult}
596613
domainState={domainState}
597614
isPublishing={isPublishing}

apps/builder/app/builder/features/topbar/publish.tsx

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ import { validateProjectDomain, type Project } from "@webstudio-is/project";
4343
import { $authPermit, $project, $publishedOrigin } from "~/shared/nano-states";
4444
import {
4545
Domains,
46-
getPublishStatusAndText,
4746
getStatus,
4847
type Domain,
4948
PENDING_TIMEOUT,
49+
getPublishStatusAndTextNew,
5050
} from "./domains";
5151
import { CollapsibleDomainSection } from "./collapsible-domain-section";
5252
import {
@@ -154,8 +154,8 @@ const ChangeProjectDomain = ({
154154
};
155155

156156
const { statusText, status } =
157-
project.latestBuild != null
158-
? getPublishStatusAndText(project.latestBuild)
157+
project.latestBuildVirtual != null
158+
? getPublishStatusAndTextNew(project.latestBuildVirtual)
159159
: {
160160
statusText: "Not published",
161161
status: "PENDING" as const,
@@ -335,7 +335,7 @@ const Publish = ({
335335
{
336336
projectId: project.id,
337337
domains: domainsToPublish.map(
338-
(projectDomain) => projectDomain.domain.domain
338+
(projectDomain) => projectDomain.domain
339339
),
340340
destination: "saas",
341341
},
@@ -355,7 +355,10 @@ const Publish = ({
355355
const getStaticPublishStatusAndText = ({
356356
updatedAt,
357357
publishStatus,
358-
}: Pick<NonNullable<Domain["latestBuid"]>, "updatedAt" | "publishStatus">) => {
358+
}: {
359+
updatedAt: string;
360+
publishStatus: string;
361+
}) => {
359362
let status = publishStatus;
360363

361364
const delta = Date.now() - new Date(updatedAt).getTime();
@@ -572,13 +575,6 @@ const Content = (props: {
572575
}) => {
573576
const [newDomains, setNewDomains] = useState(new Set<string>());
574577

575-
const {
576-
data: domainsResult,
577-
load: domainRefresh,
578-
state: domainState,
579-
error: domainSystemError,
580-
} = trpcClient.domain.findMany.useQuery();
581-
582578
const {
583579
load: projectLoad,
584580
data: projectData,
@@ -588,40 +584,27 @@ const Content = (props: {
588584

589585
useEffect(() => {
590586
projectLoad({ projectId: props.projectId });
591-
domainRefresh({ projectId: props.projectId });
592-
}, [domainRefresh, props.projectId, projectLoad]);
587+
}, [props.projectId, projectLoad]);
588+
589+
projectData?.success;
593590

594591
const domainsToPublish = useMemo(
595592
() =>
596-
domainsResult?.success
597-
? domainsResult.data.filter(
593+
projectData?.success
594+
? projectData.project.domainsVirtual.filter(
598595
(projectDomain) => getStatus(projectDomain) === "VERIFIED_ACTIVE"
599596
)
600597
: [],
601-
[domainsResult]
598+
[projectData]
602599
);
603600

604-
const latestBuilds = useMemo(
605-
() => [
606-
projectData?.success ? (projectData.project.latestBuild ?? null) : null,
607-
...domainsToPublish.map((domain) => domain.latestBuid),
608-
],
609-
[domainsToPublish, projectData]
610-
);
601+
const latestBuildVirtual = projectData?.success
602+
? (projectData.project.latestBuildVirtual ?? undefined)
603+
: undefined;
611604

612-
const hasPendingState = useMemo(
613-
() =>
614-
latestBuilds.some((latestBuild) => {
615-
if (latestBuild === null) {
616-
return false;
617-
}
618-
const { status } = getPublishStatusAndText(latestBuild);
619-
if (status === "PENDING") {
620-
return true;
621-
}
622-
}),
623-
[latestBuilds]
624-
);
605+
const hasPendingState = latestBuildVirtual
606+
? getPublishStatusAndTextNew(latestBuildVirtual).status === "PENDING"
607+
: false;
625608

626609
const [isPublishing, setIsPublishing] = useState(hasPendingState);
627610

@@ -669,31 +652,23 @@ const Content = (props: {
669652
/>
670653
)}
671654

672-
{domainsResult?.success === true && (
655+
{projectData?.success && (
673656
<Domains
674657
newDomains={newDomains}
675-
domains={domainsResult.data}
676-
refreshDomainResult={domainRefresh}
677-
domainState={domainState}
658+
domains={projectData.project.domainsVirtual}
659+
refreshDomainResult={projectLoad}
660+
domainState={projectState}
678661
isPublishing={isPublishing}
679662
/>
680663
)}
681-
682-
{domainSystemError !== undefined && (
683-
<ErrorText>{domainSystemError}</ErrorText>
684-
)}
685-
686-
{domainsResult?.success === false && (
687-
<ErrorText>{domainsResult.error}</ErrorText>
688-
)}
689664
</ScrollArea>
690665
<Flex direction="column" justify="end" css={{ height: 0 }}>
691666
<Separator />
692667
</Flex>
693668
<AddDomain
694669
projectId={props.projectId}
695-
refreshDomainResult={domainRefresh}
696-
domainState={domainState}
670+
refreshDomainResult={projectLoad}
671+
domainState={projectState}
697672
onCreate={(domain) => {
698673
setNewDomains((prev) => {
699674
return new Set([...prev, domain]);
@@ -708,7 +683,6 @@ const Content = (props: {
708683
domainsToPublish={domainsToPublish}
709684
refresh={() => {
710685
projectLoad({ projectId: props.projectId });
711-
domainRefresh({ projectId: props.projectId });
712686
}}
713687
isPublishing={isPublishing}
714688
setIsPublishing={setIsPublishing}

0 commit comments

Comments
 (0)