Add domains to publish (#1694)

## Description

At publish we would allow to check domains we want to publish. As of now
we publish all project domains.

But already pass them from UI, so future move would be just UI changes,
without touching API

- [x] - Fix "no access to project domains using service api call"

## 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-builder/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env.example`
and the `builder/env-check.js` if mandatory
This commit is contained in:
Ivan Starkov authored and GitHub committed 2023-06-01 17:59:10 +04:00
1 parent 54a9303a47
commit 00c02201a0
11 files changed
+80 -23

No files matched your search

@@ -67,11 +67,14 @@ const getCname = (domain: string) => {
return cnameArray.join(".");
};
const getStatusText = (props: { projectDomain: Domain }) => {
const status = props.projectDomain.verified
? (`VERIFIED_${props.projectDomain.domain.status}` as `VERIFIED_${DomainStatus}`)
export const getStatus = (projectDomain: Domain) =>
projectDomain.verified
? (`VERIFIED_${projectDomain.domain.status}` as const)
: `UNVERIFIED`;
const getStatusText = (props: { projectDomain: Domain }) => {
const status = getStatus(props.projectDomain);
let isVerifiedActive = false;
let text = "Something went wrong";
@@ -23,7 +23,7 @@ import { getPublishedUrl } from "~/shared/router-utils";
import { theme } from "@webstudio-is/design-system";
import { useAuthPermit } from "~/shared/nano-states";
import { isFeatureEnabled } from "@webstudio-is/feature-flags";
import { Domains } from "./domains";
import { Domains, getStatus } from "./domains";
import { CollapsibleDomainSection } from "./collapsible-domain-section";
import {
CheckCircleIcon,
@@ -207,7 +207,13 @@ const ChangeProjectDomain = (props: PublishButtonProps) => {
);
};
const Publish = ({ projectId }: { projectId: Project["id"] }) => {
const Publish = ({
projectId,
domains,
}: {
projectId: Project["id"];
domains: string[];
}) => {
const {
send: publish,
state: publishState,
@@ -239,7 +245,7 @@ const Publish = ({ projectId }: { projectId: Project["id"] }) => {
color="positive"
disabled={publishState !== "idle"}
onClick={() => {
publish({ projectId });
publish({ projectId, domains });
}}
>
Publish
@@ -260,6 +266,16 @@ const Content = (props: PublishButtonProps) => {
refreshDomainResult({ projectId: props.project.id });
}, [refreshDomainResult, props.project.id]);
// In the future we would allow to select what domains to publish,
// now we just publish all verified and active domains
const domainsToPublish = domainsResult?.success
? domainsResult.data
.filter(
(projectDomain) => getStatus(projectDomain) === "VERIFIED_ACTIVE"
)
.map((projectDomain) => projectDomain.domain.domain)
: [];
return (
<>
<ScrollArea>
@@ -308,7 +324,7 @@ const Content = (props: PublishButtonProps) => {
</>
)}
<Publish projectId={props.project.id} />
<Publish projectId={props.project.id} domains={domainsToPublish} />
</>
);
};
@@ -21,7 +21,7 @@ export const loader = async ({
throw json("Required project id", { status: 400 });
}
const context = await createContext(request, "prod");
const context = await createContext(request);
const pagesCanvasData = await loadProductionCanvasData(buildId, context);
+8 -8
View File
@@ -4,8 +4,7 @@ import { authenticator } from "~/services/auth.server";
import { trpcClient } from "~/services/trpc.server";
const createAuthorizationContext = async (
request: Request,
buildEnv: AppContext["authorization"]["buildEnv"]
request: Request
): Promise<AppContext["authorization"]> => {
const url = new URL(request.url);
@@ -13,10 +12,14 @@ const createAuthorizationContext = async (
const user = await authenticator.isAuthenticated(request);
const isServiceCall =
request.headers.has("Authorization") &&
request.headers.get("Authorization") === env.TRPC_SERVER_API_TOKEN;
const context: AppContext["authorization"] = {
userId: user?.id,
authToken,
buildEnv,
isServiceCall,
authorizeTrpc: trpcClient.authorize,
};
@@ -48,11 +51,8 @@ const createDeploymentContext = (request: Request) => {
/**
* argument buildEnv==="prod" only if we are loading project with production build
*/
export const createContext = async (
request: Request,
buildEnv: AppContext["authorization"]["buildEnv"] = "dev"
): Promise<AppContext> => {
const authorization = await createAuthorizationContext(request, buildEnv);
export const createContext = async (request: Request): Promise<AppContext> => {
const authorization = await createAuthorizationContext(request);
const domain = createDomainContext(request);
const deployment = createDeploymentContext(request);
+36 -1
View File
@@ -4,6 +4,7 @@ import { loadAssetsByProject } from "@webstudio-is/asset-uploader/index.server";
import type { AppContext } from "@webstudio-is/trpc-interface/index.server";
import { findPageByIdOrPath } from "@webstudio-is/project-build";
import type { Build } from "@webstudio-is/prisma-client";
import { db as domainDb } from "@webstudio-is/domain/index.server";
export const loadProductionCanvasData = async (
buildId: Build["id"],
@@ -15,6 +16,34 @@ export const loadProductionCanvasData = async (
throw new Error("The project is not published");
}
const { deployment } = build;
if (deployment === undefined) {
throw new Error("The project is not published");
}
const currentProjectDomainsResult = await domainDb.findMany(
{ projectId: build.projectId },
context
);
if (currentProjectDomainsResult.success === false) {
throw new Error(currentProjectDomainsResult.error);
}
const currentProjectDomains = currentProjectDomainsResult.data;
// Check that build deployment domains are still active and verified
// for examle: redeploy created few days later
const domains = deployment.domains.filter((domain) =>
currentProjectDomains.some(
(projectDomain) =>
projectDomain.domain.domain === domain &&
projectDomain.domain.status === "ACTIVE" &&
projectDomain.verified
)
);
const page = findPageByIdOrPath(build.pages, "/");
if (page === undefined) {
@@ -24,7 +53,13 @@ export const loadProductionCanvasData = async (
const allAssets = await loadAssetsByProject(build.projectId, context);
const canvasData = {
build,
build: {
...build,
deployment: {
...deployment,
domains,
},
},
page,
pages: [build.pages.homePage, ...build.pages.pages],
};
+1 -1
View File
@@ -32,7 +32,7 @@ export const findMany = async (
> => {
// Only builder of the project can list domains
const canList = await authorizeProject.hasProjectPermit(
{ projectId: props.projectId, permit: "build" },
{ projectId: props.projectId, permit: "view" },
context
);
+1
View File
@@ -1 +1,2 @@
export * from "./trpc";
export * from "./db";
+2 -2
View File
@@ -25,14 +25,14 @@ export const domainRouter = router({
}
}),
publish: procedure
.input(z.object({ projectId: z.string() }))
.input(z.object({ projectId: z.string(), domains: z.array(z.string()) }))
.mutation(async ({ input, ctx }) => {
try {
const build = await createProductionBuild(
{
projectId: input.projectId,
deployment: {
domains: [],
domains: input.domains,
},
},
ctx
+2
View File
@@ -188,6 +188,8 @@ export const cloneBuild = async (
const data = {
...build,
id: undefined,
createdAt: undefined,
updatedAt: undefined,
deployment: props.deployment
? serializeDeployment(props.deployment)
: undefined,
@@ -47,7 +47,7 @@ export const hasProjectPermit = async (
const namespace = "Project";
// Allow load production build env i.e. "published" site
if (props.permit === "view" && context.authorization.buildEnv === "prod") {
if (props.permit === "view" && context.authorization.isServiceCall) {
return true;
}
@@ -15,9 +15,9 @@ type AuthorizationContext = {
authToken: string | undefined;
/**
* buildEnv==="prod" only if we are loading project with production build
* Allow service 2 service communications to skip authorization for view calls
*/
buildEnv: "dev" | "prod";
isServiceCall: boolean;
// Pass trpcClient through context as only main app can initialize it
authorizeTrpc: TrpcInterfaceClient["authorize"];