Skip to content

Commit f673c05

Browse files
authored
Merge pull request #137 from peelar/misc
misc
2 parents 26b7e70 + 2411115 commit f673c05

9 files changed

Lines changed: 122 additions & 14 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"dopeshot-app": patch
3+
---
4+
5+
Fix image export failures when layouts include signed logo URLs by avoiding cache-busting on signed assets.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ yarn-error.log*
3838
.env
3939
.env.test
4040
.env.local
41+
.env.prod
4142
.env.development.local
4243
.env.test.local
4344
.env.production.local

apps/app/src/app/brand/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { redirect } from "next/navigation";
22
import { PlaygroundPage } from "@/app/(playground)/_components/playground-page";
33
import { verifySession } from "@/lib/auth/session";
44
import { getUserDb } from "@/lib/data/dal";
5+
import { FORCE_ONBOARDING_DEV } from "@/lib/dev-flags";
56
import { getUserTier } from "@/lib/tier";
67
import { isBrandPersonality } from "@/lib/types/brand";
78

@@ -60,7 +61,7 @@ export default async function Page() {
6061
looksCompleteFromProfile(profile);
6162
}
6263

63-
const initialOnboardingOpen = tier === "brand" && !onboardingComplete;
64+
const initialOnboardingOpen = FORCE_ONBOARDING_DEV || (tier === "brand" && !onboardingComplete);
6465

6566
return (
6667
<PlaygroundPage

apps/app/src/app/m/[itemId]/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { redirect } from "next/navigation";
22
import { PlaygroundPage } from "@/app/(playground)/_components/playground-page";
33
import { verifySession } from "@/lib/auth/session";
44
import { getUserDb } from "@/lib/data/dal";
5+
import { FORCE_ONBOARDING_DEV } from "@/lib/dev-flags";
56
import { getUserTier } from "@/lib/tier";
67
import { isBrandPersonality } from "@/lib/types/brand";
78

@@ -82,7 +83,7 @@ export default async function Page({ params }: PageProps) {
8283
looksCompleteFromProfile(profile);
8384
}
8485

85-
const initialOnboardingOpen = tier === "brand" && !onboardingComplete;
86+
const initialOnboardingOpen = FORCE_ONBOARDING_DEV || (tier === "brand" && !onboardingComplete);
8687

8788
return (
8889
<PlaygroundPage

apps/app/src/app/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PlaygroundPage } from "@/app/(playground)/_components/playground-page";
22
import { verifySession } from "@/lib/auth/session";
33
import { getUserDb } from "@/lib/data/dal";
4+
import { FORCE_ONBOARDING_DEV } from "@/lib/dev-flags";
45
import { getUserTier } from "@/lib/tier";
56
import { isBrandPersonality } from "@/lib/types/brand";
67

@@ -57,7 +58,7 @@ export default async function Page() {
5758
looksCompleteFromProfile(profile);
5859
}
5960

60-
const initialOnboardingOpen = tier === "brand" && !onboardingComplete;
61+
const initialOnboardingOpen = FORCE_ONBOARDING_DEV || (tier === "brand" && !onboardingComplete);
6162

6263
return (
6364
<PlaygroundPage

apps/app/src/domain/layout/export.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ type ExportSizeOptions = {
4242
maxImageScale?: number;
4343
};
4444

45+
function isSignedUrl(url: string): boolean {
46+
if (!url) return false;
47+
return (
48+
url.includes("token=") ||
49+
url.includes("X-Amz-Signature=") ||
50+
url.includes("X-Amz-Algorithm=") ||
51+
url.includes("X-Amz-Credential=") ||
52+
url.includes("X-Amz-Date=")
53+
);
54+
}
55+
56+
function elementHasSignedUrlImages(element: HTMLElement): boolean {
57+
const images = Array.from(element.querySelectorAll("img"));
58+
return images.some((img) => isSignedUrl(img.currentSrc || img.src));
59+
}
60+
4561
/**
4662
* Calculate the pixel ratio for export, clamped to [1, 3].
4763
* Defaults to device pixel ratio or 2 (whichever is higher).
@@ -100,14 +116,16 @@ export async function exportLayoutAsPngWithBlob(
100116
try {
101117
await waitForRender();
102118

119+
const hasSignedUrlImages = elementHasSignedUrlImages(element);
103120
const resolvedPixelRatio = calculatePixelRatio({
104121
desiredPixelRatio: pixelRatio,
105122
maxImageScale,
106123
devicePixelRatio: window.devicePixelRatio,
107124
});
108125

109126
const dataUrl = await toPng(element, {
110-
cacheBust: true,
127+
// Signed URLs become invalid if we append cache-busting query params.
128+
cacheBust: !hasSignedUrlImages,
111129
pixelRatio: resolvedPixelRatio,
112130
width,
113131
height,
@@ -201,10 +219,11 @@ export async function generateThumbnail(elementId: string): Promise<string> {
201219
try {
202220
await waitForRender();
203221

222+
const hasSignedUrlImages = elementHasSignedUrlImages(element);
204223
// Capture full element at low resolution for thumbnail
205224
// CSS handles display sizing - we just need a lightweight capture
206225
const dataUrl = await toPng(element, {
207-
cacheBust: true,
226+
cacheBust: !hasSignedUrlImages,
208227
pixelRatio: 0.5, // Half resolution for smaller file size
209228
style: {
210229
visibility: "visible",

apps/app/src/hooks/use-onboarding-status.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
import { useCallback, useEffect, useMemo, useState } from "react";
44

55
import { useSession } from "@/lib/auth/auth-client";
6+
import { FORCE_ONBOARDING_DEV } from "@/lib/dev-flags";
67

78
type OnboardingStatusPayload = {
89
tier: "free" | "brand";
910
onboardingComplete: boolean;
1011
};
1112

12-
const FORCE_ONBOARDING_DEV =
13-
process.env.NODE_ENV === "development" && process.env.NEXT_PUBLIC_FORCE_ONBOARDING === "true";
14-
1513
export function useOnboardingStatus(options?: { enabled?: boolean }) {
1614
const { data: session, isPending } = useSession();
1715
const enabled = options?.enabled ?? true;

apps/app/src/lib/dev-flags.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const isDevelopment = process.env.NODE_ENV === "development";
2+
3+
export const FORCE_ONBOARDING_DEV =
4+
isDevelopment && process.env.NEXT_PUBLIC_FORCE_ONBOARDING === "true";

scripts/sql/run.sh

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ strip_quotes() {
9393
echo "$s"
9494
}
9595

96+
set_sql_url() {
97+
local value="$1"
98+
local label="$2"
99+
local cleaned
100+
cleaned="$(strip_quotes "$value")"
101+
if [[ -z "$SQL_URL" && -n "$cleaned" ]]; then
102+
SQL_URL="$cleaned"
103+
SQL_URL_SOURCE="$label"
104+
fi
105+
}
106+
96107
read_env_value() {
97108
local key="$1"
98109
local file="$2"
@@ -102,10 +113,13 @@ read_env_value() {
102113
}
103114

104115
SQL_URL=""
116+
SQL_URL_SOURCE=""
105117
ENV_FILE=""
118+
ENVIRONMENT_UPPER="$(echo "${ENVIRONMENT:-}" | tr '[:lower:]' '[:upper:]')"
106119

107120
if [[ -n "$URL_OVERRIDE" ]]; then
108121
SQL_URL="$(strip_quotes "$URL_OVERRIDE")"
122+
SQL_URL_SOURCE="--url"
109123
ENV_FILE="(override: --url)"
110124
else
111125
if [[ "$ENVIRONMENT" != "staging" && "$ENVIRONMENT" != "prod" && "$ENVIRONMENT" != "local" ]]; then
@@ -115,37 +129,96 @@ else
115129

116130
if [[ "$ENVIRONMENT" == "staging" ]]; then
117131
ENV_FILE="${STAGING_ENV_FILE:-${ENV_FILE:-$APP_DIR/.env.local}}"
132+
ENV_DIRECT_FROM_FILE="$(read_env_value "STAGING_DIRECT_DATABASE_URL" "$ENV_FILE")"
118133
DIRECT_FROM_FILE="$(read_env_value "DIRECT_DATABASE_URL" "$ENV_FILE")"
119134
DIRECT_ALT_FROM_FILE="$(read_env_value "DIRECT_URL" "$ENV_FILE")"
120-
SQL_URL="${STAGING_DIRECT_DATABASE_URL:-${DIRECT_DATABASE_URL:-${DIRECT_URL:-${DIRECT_FROM_FILE:-$DIRECT_ALT_FROM_FILE}}}}"
135+
set_sql_url "${STAGING_DIRECT_DATABASE_URL:-}" "STAGING_DIRECT_DATABASE_URL (env)"
136+
set_sql_url "${DIRECT_DATABASE_URL:-}" "DIRECT_DATABASE_URL (env)"
137+
set_sql_url "${DIRECT_URL:-}" "DIRECT_URL (env)"
138+
set_sql_url "${ENV_DIRECT_FROM_FILE:-}" "STAGING_DIRECT_DATABASE_URL ($ENV_FILE)"
139+
set_sql_url "${DIRECT_FROM_FILE:-}" "DIRECT_DATABASE_URL ($ENV_FILE)"
140+
set_sql_url "${DIRECT_ALT_FROM_FILE:-}" "DIRECT_URL ($ENV_FILE)"
121141
elif [[ "$ENVIRONMENT" == "prod" ]]; then
122142
ENV_FILE="${PROD_ENV_FILE:-$APP_DIR/.env.prod}"
143+
if [[ ! -f "$ENV_FILE" && -z "${PROD_DIRECT_DATABASE_URL:-}" && -z "${DIRECT_DATABASE_URL:-}" && -z "${DIRECT_URL:-}" ]]; then
144+
cat <<EOF
145+
Error: prod env file not found: $ENV_FILE
146+
147+
Set one of the following:
148+
- Create $ENV_FILE (recommended)
149+
- Set PROD_ENV_FILE=/path/to/env
150+
- Export PROD_DIRECT_DATABASE_URL in your shell
151+
- Use --url 'postgresql://...'
152+
EOF
153+
exit 1
154+
fi
155+
ALLOW_PROD_URL=0
156+
if [[ "${PROD_SQL_CONFIRMED:-}" == "yes" || "${npm_lifecycle_event:-}" == "sql:prod" ]]; then
157+
ALLOW_PROD_URL=1
158+
fi
159+
if [[ "$ALLOW_PROD_URL" -eq 1 ]]; then
160+
ENV_DIRECT_FROM_FILE="$(read_env_value "PROD_DIRECT_DATABASE_URL" "$ENV_FILE")"
161+
PROD_DIRECT_FROM_ENV="${PROD_DIRECT_DATABASE_URL:-}"
162+
else
163+
ENV_DIRECT_FROM_FILE=""
164+
PROD_DIRECT_FROM_ENV=""
165+
fi
123166
DIRECT_FROM_FILE="$(read_env_value "DIRECT_DATABASE_URL" "$ENV_FILE")"
124167
DIRECT_ALT_FROM_FILE="$(read_env_value "DIRECT_URL" "$ENV_FILE")"
125-
SQL_URL="${PROD_DIRECT_DATABASE_URL:-${DIRECT_DATABASE_URL:-${DIRECT_URL:-${DIRECT_FROM_FILE:-$DIRECT_ALT_FROM_FILE}}}}"
168+
set_sql_url "${PROD_DIRECT_FROM_ENV:-}" "PROD_DIRECT_DATABASE_URL (env)"
169+
set_sql_url "${DIRECT_DATABASE_URL:-}" "DIRECT_DATABASE_URL (env)"
170+
set_sql_url "${DIRECT_URL:-}" "DIRECT_URL (env)"
171+
set_sql_url "${ENV_DIRECT_FROM_FILE:-}" "PROD_DIRECT_DATABASE_URL ($ENV_FILE)"
172+
set_sql_url "${DIRECT_FROM_FILE:-}" "DIRECT_DATABASE_URL ($ENV_FILE)"
173+
set_sql_url "${DIRECT_ALT_FROM_FILE:-}" "DIRECT_URL ($ENV_FILE)"
126174
else
127175
ENV_FILE="${LOCAL_ENV_FILE:-${ENV_FILE:-$APP_DIR/.env.local}}"
128176
DIRECT_FROM_FILE="$(read_env_value "DIRECT_DATABASE_URL" "$ENV_FILE")"
129177
DIRECT_ALT_FROM_FILE="$(read_env_value "DIRECT_URL" "$ENV_FILE")"
130-
SQL_URL="${DIRECT_DATABASE_URL:-${DIRECT_URL:-${DIRECT_FROM_FILE:-$DIRECT_ALT_FROM_FILE}}}"
178+
set_sql_url "${DIRECT_DATABASE_URL:-}" "DIRECT_DATABASE_URL (env)"
179+
set_sql_url "${DIRECT_URL:-}" "DIRECT_URL (env)"
180+
set_sql_url "${DIRECT_FROM_FILE:-}" "DIRECT_DATABASE_URL ($ENV_FILE)"
181+
set_sql_url "${DIRECT_ALT_FROM_FILE:-}" "DIRECT_URL ($ENV_FILE)"
131182
fi
132-
133-
SQL_URL="$(strip_quotes "${SQL_URL:-}")"
134183
fi
135184

136185
if [[ -z "$SQL_URL" ]]; then
137186
cat <<EOF
138187
No database URL found.
139188
140189
Set one of the following:
190+
- ${ENVIRONMENT_UPPER}_DIRECT_DATABASE_URL in $ENV_FILE
141191
- DIRECT_DATABASE_URL in $ENV_FILE
142192
- DIRECT_URL in $ENV_FILE
143-
- ${ENVIRONMENT^^}_DIRECT_DATABASE_URL in your shell
193+
- ${ENVIRONMENT_UPPER}_DIRECT_DATABASE_URL in your shell
144194
- Use --url 'postgresql://...'
145195
EOF
196+
if [[ "$ENVIRONMENT" == "prod" && "${ALLOW_PROD_URL:-0}" -eq 0 ]]; then
197+
echo
198+
echo "Note: PROD_DIRECT_DATABASE_URL is only read when running via 'pnpm sql:prod' (or with PROD_SQL_CONFIRMED=yes)."
199+
fi
200+
exit 1
201+
fi
202+
203+
if [[ "$SQL_URL" == =* ]]; then
204+
echo "Error: invalid database URL (starts with '='). Check for a double '=' in your env file."
146205
exit 1
147206
fi
148207

208+
if [[ "$SQL_URL" =~ [[:space:]] ]]; then
209+
echo "Error: invalid database URL (contains whitespace)."
210+
exit 1
211+
fi
212+
213+
case "$SQL_URL" in
214+
postgres://*|postgresql://*)
215+
;;
216+
*)
217+
echo "Error: invalid database URL (expected postgres:// or postgresql://)."
218+
exit 1
219+
;;
220+
esac
221+
149222
if [[ "$ENVIRONMENT" == "prod" && "$DRY_RUN" -eq 0 ]]; then
150223
if [[ "${PROD_SQL_CONFIRMED:-}" != "yes" ]]; then
151224
cat <<'EOF'
@@ -193,6 +266,11 @@ if [[ "$DRY_RUN" -eq 1 ]]; then
193266
echo " Environment: $ENVIRONMENT"
194267
echo " Script: $SCRIPT_PATH"
195268
echo " Env file: $ENV_FILE"
269+
if [[ -n "$SQL_URL_SOURCE" ]]; then
270+
echo " URL source: $SQL_URL_SOURCE"
271+
else
272+
echo " URL source: (unknown)"
273+
fi
196274
if [[ "${VAR_KEYS[*]-}" != "" ]]; then
197275
echo " Vars: ${VAR_KEYS[*]}"
198276
else

0 commit comments

Comments
 (0)