diff --git a/.github/actions/vercel/action.yaml b/.github/actions/vercel/action.yaml index 9b0119f54184..b1f2c20e72f8 100644 --- a/.github/actions/vercel/action.yaml +++ b/.github/actions/vercel/action.yaml @@ -76,7 +76,11 @@ runs: export GITHUB_SHA=${{ inputs.sha }} export GITHUB_REF_NAME=${{ inputs.ref-name }} - pnpx vercel build + if [ "${{ inputs.environment }}" = "production" ]; then + pnpx vercel build --prod + else + pnpx vercel build + fi shell: bash - name: Patch @@ -103,10 +107,19 @@ runs: - name: Deploy id: deploy run: | - pnpx vercel deploy \ - --prebuilt \ - --token ${{ inputs.vercel-token }} \ - 2> >(tee info.txt >&2) | tee domain.txt + if [ "${{ inputs.environment }}" = "production" ]; then + pnpx vercel deploy \ + --prebuilt \ + --prod \ + --skip-domain \ + --token ${{ inputs.vercel-token }} \ + 2> >(tee info.txt >&2) | tee domain.txt + else + pnpx vercel deploy \ + --prebuilt \ + --token ${{ inputs.vercel-token }} \ + 2> >(tee info.txt >&2) | tee domain.txt + fi echo "domain=$(cat ./domain.txt)" >> $GITHUB_OUTPUT echo "inspect-url=$(cat info.txt | grep 'Inspect:' | awk '{print $2}')" >> $GITHUB_OUTPUT @@ -114,6 +127,7 @@ runs: shell: bash - name: Set Alias + if: ${{ inputs.environment != 'production' }} id: alias run: | ALIAS="${{ steps.branch.outputs.value }}" diff --git a/.github/workflows/vercel-deploy-staging.yml b/.github/workflows/vercel-deploy-staging.yml index a96527e72bfb..51e6d23bf227 100644 --- a/.github/workflows/vercel-deploy-staging.yml +++ b/.github/workflows/vercel-deploy-staging.yml @@ -87,6 +87,18 @@ jobs: sha: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} environment: ${{ matrix.environment }} + - uses: ./.github/actions/vercel + if: matrix.environment == 'staging' + id: vercel-prod + name: Deploy to Vercel + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + ref-name: ${{ github.ref_name }} + sha: ${{ github.sha }} + environment: "production" + - name: Debug Vercel Outputs run: | echo "domain=${{ steps.vercel.outputs.domain }}" @@ -99,6 +111,13 @@ jobs: description: "[${{ matrix.environment }}] Vercel logs" url: "${{ steps.vercel.outputs.inspect-url }}" + - uses: ./.github/actions/add-status + if: matrix.environment == 'staging' + with: + title: "⏰ [${{ matrix.environment }}] Vercel Production Inspection" + description: "[${{ matrix.environment }}] Vercel production logs" + url: "${{ steps.vercel-prod.outputs.inspect-url }}" + - uses: ./.github/actions/add-status with: title: "⭐ [${{ matrix.environment }}] Apps Webstudio URL" diff --git a/apps/builder/.env b/apps/builder/.env index 53c8d18477c2..1f68cabc83f9 100644 --- a/apps/builder/.env +++ b/apps/builder/.env @@ -74,7 +74,7 @@ PLANS='[ "extends": "Pro", "features": { "maxWorkspaces": 20, - "minSeats": 4, + "seatsIncluded": 4, "maxSeatsPerWorkspace": 20 } } diff --git a/apps/builder/app/services/workspace-router.server.ts b/apps/builder/app/services/workspace-router.server.ts index 9966d036e6d5..5989894542d6 100644 --- a/apps/builder/app/services/workspace-router.server.ts +++ b/apps/builder/app/services/workspace-router.server.ts @@ -6,8 +6,7 @@ import { } from "@webstudio-is/trpc-interface/index.server"; import { workspace as workspaceApi } from "@webstudio-is/project/index.server"; import { roles } from "@webstudio-is/trpc-interface/authorize"; -import { getPlanInfo, getPaidSeats } from "@webstudio-is/plans/index.server"; -import { defaultPlanFeatures } from "@webstudio-is/plans"; +import { getPaidSeats } from "@webstudio-is/plans/index.server"; import env from "~/env/env.server"; const Name = z.string().min(2).max(100); @@ -19,16 +18,10 @@ type UpdateSeatsResult = const updateSeats = async ({ userId, - subscriptionId, newQuantity, - minSeats, - maxSeats, }: { userId: string; - subscriptionId: string; newQuantity: number; - minSeats: number; - maxSeats: number; }): Promise => { if (!env.PAYMENT_WORKER_URL || !env.PAYMENT_WORKER_TOKEN) { return null; @@ -44,10 +37,7 @@ const updateSeats = async ({ }, body: JSON.stringify({ userId, - subscriptionId, newQuantity, - minSeats, - maxSeats, }), }); @@ -86,25 +76,11 @@ const syncOwnerSeats = async ( } const ownerId = workspaceResult.data.userId; - const planResults = await getPlanInfo([ownerId], ctx); - const { planFeatures, purchases } = planResults.get(ownerId) ?? { - planFeatures: defaultPlanFeatures, - purchases: [], - }; - - const subscription = purchases.find((p) => p.subscriptionId !== undefined); - if (subscription?.subscriptionId === undefined) { - return; - } - const memberCount = await workspaceApi.countAllMembers(ownerId, ctx); const result = await updateSeats({ userId: ownerId, - subscriptionId: subscription.subscriptionId, newQuantity: memberCount + countDelta, - minSeats: planFeatures.minSeats, - maxSeats: planFeatures.maxSeatsPerWorkspace, }); if (result === null) { @@ -336,9 +312,9 @@ export const workspaceRouter = router({ success: true as const, data: { ...members, - // Falls back to minSeats (seats included in the plan) when no + // Falls back to seatsIncluded (seats included in the plan) when no // subscription event exists yet (free plan, AppSumo, etc.). - maxSeats: paidSeats ?? ctx.planFeatures.minSeats, + maxSeats: paidSeats ?? ctx.planFeatures.seatsIncluded, }, }; } catch (error) { diff --git a/apps/builder/app/shared/permissions.test.ts b/apps/builder/app/shared/permissions.test.ts index 66b375547e91..1e6429653311 100644 --- a/apps/builder/app/shared/permissions.test.ts +++ b/apps/builder/app/shared/permissions.test.ts @@ -30,7 +30,7 @@ const proPlan = { maxWorkspaces: 0, maxProjectsAllowedPerUser: 100, maxAssetsPerProject: 500, - minSeats: 0, + seatsIncluded: 0, maxSeatsPerWorkspace: 0, }; @@ -47,7 +47,7 @@ const freePlan = { maxWorkspaces: 0, maxProjectsAllowedPerUser: 2, maxAssetsPerProject: 50, - minSeats: 0, + seatsIncluded: 0, maxSeatsPerWorkspace: 0, }; diff --git a/packages/css-engine/src/core/to-value.test.ts b/packages/css-engine/src/core/to-value.test.ts index 8319ed53f59b..cf84b7f5e193 100644 --- a/packages/css-engine/src/core/to-value.test.ts +++ b/packages/css-engine/src/core/to-value.test.ts @@ -446,7 +446,6 @@ describe("Convert WS CSS Values to native CSS strings", () => { expect(value).toBe(expected); }); } - }); test("color in tuple", () => { const value = toValue({ diff --git a/packages/css-engine/src/core/to-value.ts b/packages/css-engine/src/core/to-value.ts index cd4dec85b07e..16c83dc8661e 100644 --- a/packages/css-engine/src/core/to-value.ts +++ b/packages/css-engine/src/core/to-value.ts @@ -122,7 +122,7 @@ export const toValue = ( case "oklch": return `oklch(${c1} ${c2} ${c3} / ${alpha})`; // Fall back to color() function for less common color spaces. - // colorjs uses internal short names that differ from CSS predefined color space identifiers. + // Webstudio uses colorjs internal names; map to CSS predefined color space names. case "p3": return `color(display-p3 ${c1} ${c2} ${c3} / ${alpha})`; case "a98rgb": diff --git a/packages/plans/src/plan-features.ts b/packages/plans/src/plan-features.ts index 1438154258e9..ef06b01c8d36 100644 --- a/packages/plans/src/plan-features.ts +++ b/packages/plans/src/plan-features.ts @@ -18,7 +18,7 @@ export const PlanFeaturesSchema = z.object({ maxWorkspaces: z.number().nonnegative(), maxProjectsAllowedPerUser: z.number().nonnegative(), maxAssetsPerProject: z.number().nonnegative(), - minSeats: z.number().nonnegative(), + seatsIncluded: z.number().nonnegative(), maxSeatsPerWorkspace: z.number().nonnegative(), }); @@ -45,7 +45,7 @@ export const defaultPlanFeatures: PlanFeatures = { maxWorkspaces: 1, maxProjectsAllowedPerUser: 100, maxAssetsPerProject: 50, - minSeats: 0, + seatsIncluded: 0, maxSeatsPerWorkspace: 0, };