From 9897c725de27d0fa24066e13756b032a30aefa3b Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 22 Sep 2025 13:09:07 -0700 Subject: [PATCH 1/4] Updated cloud mode checks. --- next.config.ts | 12 +---- pnpm-lock.yaml | 48 +------------------ src/app/(main)/UpdateNotice.tsx | 2 +- src/app/(main)/admin/AdminLayout.tsx | 2 +- src/app/(main)/admin/layout.tsx | 2 +- src/app/(main)/links/LinkEditForm.tsx | 4 +- src/app/(main)/settings/layout.tsx | 2 +- .../[websiteId]/cohorts/CohortEditForm.tsx | 5 +- src/app/api/config/route.ts | 3 +- src/app/login/page.tsx | 2 +- src/app/logout/page.tsx | 4 +- src/components/hooks/useConfig.ts | 1 - src/components/input/ProfileButton.tsx | 5 +- src/components/input/SettingsButton.tsx | 4 +- src/components/messages.ts | 2 +- src/lib/auth.ts | 6 +-- 16 files changed, 23 insertions(+), 81 deletions(-) diff --git a/next.config.ts b/next.config.ts index 40c94fa2b..eac6f327a 100644 --- a/next.config.ts +++ b/next.config.ts @@ -5,7 +5,7 @@ const TRACKER_SCRIPT = '/script.js'; const basePath = process.env.BASE_PATH; const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT; -const cloudUrl = process.env.CLOUD_URL; +const cloudMode = !!process.env.CLOUD_MODE; const corsMaxAge = process.env.CORS_MAX_AGE; const defaultLocale = process.env.DEFAULT_LOCALE; const forceSSL = process.env.FORCE_SSL; @@ -157,20 +157,12 @@ if (trackerScriptName) { } } -if (cloudUrl) { - redirects.push({ - source: '/login', - destination: cloudUrl, - permanent: false, - }); -} - /** @type {import('next').NextConfig} */ export default { reactStrictMode: false, env: { basePath, - cloudUrl, + cloudMode, currentVersion: pkg.version, defaultLocale, }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc1199ff8..865d02cf1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -364,44 +364,7 @@ importers: specifier: ^5.9.2 version: 5.9.2 - dist: - dependencies: - chart.js: - specifier: ^4.5.0 - version: 4.5.0 - chartjs-adapter-date-fns: - specifier: ^3.0.0 - version: 3.0.0(chart.js@4.5.0)(date-fns@2.30.0) - colord: - specifier: ^2.9.2 - version: 2.9.3 - jsonwebtoken: - specifier: ^9.0.2 - version: 9.0.2 - lucide-react: - specifier: ^0.542.0 - version: 0.542.0(react@19.1.1) - pure-rand: - specifier: ^7.0.1 - version: 7.0.1 - react-simple-maps: - specifier: ^2.3.0 - version: 2.3.0(prop-types@15.8.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-use-measure: - specifier: ^2.0.4 - version: 2.1.7(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-window: - specifier: ^1.8.6 - version: 1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - serialize-error: - specifier: ^12.0.0 - version: 12.0.0 - thenby: - specifier: ^1.3.4 - version: 1.3.4 - uuid: - specifier: ^11.1.0 - version: 11.1.0 + dist: {} packages: @@ -5274,11 +5237,6 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lucide-react@0.542.0: - resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lucide-react@0.543.0: resolution: {integrity: sha512-fpVfuOQO0V3HBaOA1stIiP/A2fPCXHIleRZL16Mx3HmjTYwNSbimhnFBygs2CAfU1geexMX5ItUcWBGUaqw5CA==} peerDependencies: @@ -13394,10 +13352,6 @@ snapshots: dependencies: react: 19.1.1 - lucide-react@0.542.0(react@19.1.1): - dependencies: - react: 19.1.1 - lucide-react@0.543.0(react@19.1.1): dependencies: react: 19.1.1 diff --git a/src/app/(main)/UpdateNotice.tsx b/src/app/(main)/UpdateNotice.tsx index c971d4038..357287912 100644 --- a/src/app/(main)/UpdateNotice.tsx +++ b/src/app/(main)/UpdateNotice.tsx @@ -18,7 +18,7 @@ export function UpdateNotice({ user, config }) { !config?.updatesDisabled && !config?.privateMode && !pathname.includes('/share/') && - !process.env.cloudUrl && + !process.env.cloudMode && !dismissed; const updateCheck = useCallback(() => { diff --git a/src/app/(main)/admin/AdminLayout.tsx b/src/app/(main)/admin/AdminLayout.tsx index eb4c2ffa5..8b1387854 100644 --- a/src/app/(main)/admin/AdminLayout.tsx +++ b/src/app/(main)/admin/AdminLayout.tsx @@ -11,7 +11,7 @@ export function AdminLayout({ children }: { children: ReactNode }) { const { formatMessage, labels } = useMessages(); const { pathname } = useNavigation(); - if (!user.isAdmin || process.env.cloudUrl) { + if (!user.isAdmin || process.env.cloudMode) { return null; } diff --git a/src/app/(main)/admin/layout.tsx b/src/app/(main)/admin/layout.tsx index 3dea41422..634fc6588 100644 --- a/src/app/(main)/admin/layout.tsx +++ b/src/app/(main)/admin/layout.tsx @@ -2,7 +2,7 @@ import { Metadata } from 'next'; import { AdminLayout } from './AdminLayout'; export default function ({ children }) { - if (process.env.cloudUrl) { + if (process.env.cloudMode) { return null; } diff --git a/src/app/(main)/links/LinkEditForm.tsx b/src/app/(main)/links/LinkEditForm.tsx index 16c65aecb..23aaab1e6 100644 --- a/src/app/(main)/links/LinkEditForm.tsx +++ b/src/app/(main)/links/LinkEditForm.tsx @@ -139,7 +139,9 @@ export function LinkEditForm({ {formatMessage(labels.cancel)} )} - {formatMessage(labels.save)} + + {formatMessage(labels.save)} + ); diff --git a/src/app/(main)/settings/layout.tsx b/src/app/(main)/settings/layout.tsx index 6f409e483..fcc7392ad 100644 --- a/src/app/(main)/settings/layout.tsx +++ b/src/app/(main)/settings/layout.tsx @@ -2,7 +2,7 @@ import { Metadata } from 'next'; import { SettingsLayout } from './SettingsLayout'; export default function ({ children }) { - if (process.env.cloudUrl) { + if (process.env.cloudMode) { return null; } diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx index cfe552300..c02d85c9a 100644 --- a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx @@ -114,10 +114,7 @@ export function CohortEditForm({ - + diff --git a/src/app/api/config/route.ts b/src/app/api/config/route.ts index 7fa9ce8e2..4e40caa4b 100644 --- a/src/app/api/config/route.ts +++ b/src/app/api/config/route.ts @@ -9,8 +9,7 @@ export async function GET(request: Request) { } return json({ - cloudMode: !!process.env.CLOUD_URL, - cloudUrl: process.env.CLOUD_URL, + cloudMode: !!process.env.CLOUD_MODE, faviconUrl: process.env.FAVICON_URL, linksUrl: process.env.LINKS_URL, pixelsUrl: process.env.PIXELS_URL, diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 4c8d80446..8abf7a4e0 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -2,7 +2,7 @@ import { Metadata } from 'next'; import { LoginPage } from './LoginPage'; export default async function () { - if (process.env.DISABLE_LOGIN) { + if (process.env.DISABLE_LOGIN || process.env.CLOUD_MODE) { return null; } diff --git a/src/app/logout/page.tsx b/src/app/logout/page.tsx index 7b56ea679..0617c2e2d 100644 --- a/src/app/logout/page.tsx +++ b/src/app/logout/page.tsx @@ -1,8 +1,8 @@ -import { LogoutPage } from './LogoutPage'; import { Metadata } from 'next'; +import { LogoutPage } from './LogoutPage'; export default function () { - if (process.env.DISABLE_LOGIN) { + if (process.env.DISABLE_LOGIN || process.env.CLOUD_MODE) { return null; } diff --git a/src/components/hooks/useConfig.ts b/src/components/hooks/useConfig.ts index 170136437..643b9cbc7 100644 --- a/src/components/hooks/useConfig.ts +++ b/src/components/hooks/useConfig.ts @@ -4,7 +4,6 @@ import { useApi } from '@/components/hooks/useApi'; export type Config = { cloudMode: boolean; - cloudUrl?: string; faviconUrl?: string; linksUrl?: string; pixelsUrl?: string; diff --git a/src/components/input/ProfileButton.tsx b/src/components/input/ProfileButton.tsx index 017ea8319..03f14d071 100644 --- a/src/components/input/ProfileButton.tsx +++ b/src/components/input/ProfileButton.tsx @@ -11,14 +11,13 @@ import { Text, Row, } from '@umami/react-zen'; -import { useMessages, useLoginQuery, useNavigation, useConfig } from '@/components/hooks'; +import { useMessages, useLoginQuery, useNavigation } from '@/components/hooks'; import { LogOut, UserCircle, LockKeyhole } from '@/components/icons'; export function ProfileButton() { const { formatMessage, labels } = useMessages(); const { user } = useLoginQuery(); const { renderUrl } = useNavigation(); - const { cloudUrl } = useConfig(); const items = [ { @@ -28,7 +27,7 @@ export function ProfileButton() { icon: , }, user.isAdmin && - !cloudUrl && { + !process.env.cloudMode && { id: 'admin', label: formatMessage(labels.admin), path: '/admin', diff --git a/src/components/input/SettingsButton.tsx b/src/components/input/SettingsButton.tsx index 7e5ac4852..68fd2cdc3 100644 --- a/src/components/input/SettingsButton.tsx +++ b/src/components/input/SettingsButton.tsx @@ -16,12 +16,12 @@ export function SettingsButton() { const { formatMessage, labels } = useMessages(); const { user } = useLoginQuery(); const { router, renderUrl } = useNavigation(); - const { cloudMode, cloudUrl } = useConfig(); + const { cloudMode } = useConfig(); const handleAction = (id: Key) => { if (id === 'settings') { if (cloudMode) { - window.location.href = `${cloudUrl}/settings`; + window.location.href = `/settings`; return; } } diff --git a/src/components/messages.ts b/src/components/messages.ts index ebd8c7651..1abe98f28 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -359,7 +359,7 @@ export const labels = defineMessages({ invalidUrl: { id: 'label.invalid-url', defaultMessage: 'Invalid URL' }, environment: { id: 'label.environment', defaultMessage: 'Environment' }, criteria: { id: 'label.criteria', defaultMessage: 'Criteria' }, - share: { defaultMessage: 'label.share', id: 'Share' }, + share: { id: 'label.share', defaultMessage: 'Share' }, }); export const messages = defineMessages({ diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 46af18b8a..80fda55c0 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -18,7 +18,7 @@ export function getBearerToken(request: Request) { export async function checkAuth(request: Request) { const token = getBearerToken(request); const payload = parseSecureToken(token, secret()); - const shareToken = await parseShareToken(request.headers); + const shareToken = await parseShareToken(request); let user = null; const { userId, authKey, grant } = payload || {}; @@ -71,9 +71,9 @@ export async function hasPermission(role: string, permission: string | string[]) return ensureArray(permission).some(e => ROLE_PERMISSIONS[role]?.includes(e)); } -export function parseShareToken(headers: Headers) { +export function parseShareToken(request: Request) { try { - return parseToken(headers.get(SHARE_TOKEN_HEADER), secret()); + return parseToken(request.headers.get(SHARE_TOKEN_HEADER), secret()); } catch (e) { log(e); return null; From 5c9f97182eaf133a78096af41dd9b4cf4f989512 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 22 Sep 2025 13:57:42 -0700 Subject: [PATCH 2/4] Removed grant checks. --- src/lib/auth.ts | 9 ++++----- src/lib/types.ts | 4 +--- src/permissions/team.ts | 20 +++----------------- src/permissions/website.ts | 8 +------- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 80fda55c0..890e535f4 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -21,7 +21,7 @@ export async function checkAuth(request: Request) { const shareToken = await parseShareToken(request); let user = null; - const { userId, authKey, grant } = payload || {}; + const { userId, authKey } = payload || {}; if (userId) { user = await getUser(userId); @@ -33,7 +33,7 @@ export async function checkAuth(request: Request) { } } - log({ token, shareToken, payload, user, grant }); + log({ token, payload, authKey, shareToken, user }); if (!user?.id && !shareToken) { log('User not authorized'); @@ -45,11 +45,10 @@ export async function checkAuth(request: Request) { } return { - user, - grant, token, - shareToken, authKey, + shareToken, + user, }; } diff --git a/src/lib/types.ts b/src/lib/types.ts index c70490598..1237f5199 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,5 +1,5 @@ import { UseQueryOptions } from '@tanstack/react-query'; -import { DATA_TYPE, PERMISSIONS, ROLES, OPERATORS } from './constants'; +import { DATA_TYPE, ROLES, OPERATORS } from './constants'; import { TIME_UNIT } from './date'; export type ObjectValues = T[keyof T]; @@ -7,7 +7,6 @@ export type ObjectValues = T[keyof T]; export type ReactQueryOptions = Omit, 'queryKey' | 'queryFn'>; export type TimeUnit = ObjectValues; -export type Permission = ObjectValues; export type Role = ObjectValues; export type DynamicDataType = ObjectValues; export type Operator = (typeof OPERATORS)[keyof typeof OPERATORS]; @@ -19,7 +18,6 @@ export interface Auth { role: string; isAdmin: boolean; }; - grant?: Permission[]; shareToken?: { websiteId: string; }; diff --git a/src/permissions/team.ts b/src/permissions/team.ts index 3273c8192..77e42b1bd 100644 --- a/src/permissions/team.ts +++ b/src/permissions/team.ts @@ -3,8 +3,6 @@ import { PERMISSIONS } from '@/lib/constants'; import { getTeamUser } from '@/queries'; import { hasPermission } from '@/lib/auth'; -const cloudMode = !!process.env.CLOUD_URL; - export async function canViewTeam({ user }: Auth, teamId: string) { if (user.isAdmin) { return true; @@ -13,11 +11,7 @@ export async function canViewTeam({ user }: Auth, teamId: string) { return getTeamUser(teamId, user.id); } -export async function canCreateTeam({ user, grant }: Auth) { - if (cloudMode) { - return !!grant?.find(a => a === PERMISSIONS.teamCreate); - } - +export async function canCreateTeam({ user }: Auth) { if (user.isAdmin) { return true; } @@ -25,15 +19,11 @@ export async function canCreateTeam({ user, grant }: Auth) { return !!user; } -export async function canUpdateTeam({ user, grant }: Auth, teamId: string) { +export async function canUpdateTeam({ user }: Auth, teamId: string) { if (user.isAdmin) { return true; } - if (cloudMode) { - return !!grant?.find(a => a === PERMISSIONS.teamUpdate); - } - const teamUser = await getTeamUser(teamId, user.id); return teamUser && hasPermission(teamUser.role, PERMISSIONS.teamUpdate); @@ -49,11 +39,7 @@ export async function canDeleteTeam({ user }: Auth, teamId: string) { return teamUser && hasPermission(teamUser.role, PERMISSIONS.teamDelete); } -export async function canAddUserToTeam({ user, grant }: Auth) { - if (cloudMode) { - return !!grant?.find(a => a === PERMISSIONS.teamUpdate); - } - +export async function canAddUserToTeam({ user }: Auth) { return user.isAdmin; } diff --git a/src/permissions/website.ts b/src/permissions/website.ts index 63ae5c903..11e8dc650 100644 --- a/src/permissions/website.ts +++ b/src/permissions/website.ts @@ -3,8 +3,6 @@ import { PERMISSIONS } from '@/lib/constants'; import { hasPermission } from '@/lib/auth'; import { getTeamUser, getWebsite } from '@/queries'; -const cloudMode = !!process.env.CLOUD_URL; - export async function canViewWebsite({ user, shareToken }: Auth, websiteId: string) { if (user?.isAdmin) { return true; @@ -33,11 +31,7 @@ export async function canViewAllWebsites({ user }: Auth) { return user.isAdmin; } -export async function canCreateWebsite({ user, grant }: Auth) { - if (cloudMode) { - return !!grant?.find(a => a === PERMISSIONS.websiteCreate); - } - +export async function canCreateWebsite({ user }: Auth) { if (user.isAdmin) { return true; } From 656fb8a3d1264e6c1c752deca9ae884b835d882a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 22 Sep 2025 14:10:35 -0700 Subject: [PATCH 3/4] Removed duplicate saved messages. --- src/app/(main)/links/LinkAddButton.tsx | 15 ++++----------- src/app/(main)/pixels/PixelAddButton.tsx | 15 ++++----------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/app/(main)/links/LinkAddButton.tsx b/src/app/(main)/links/LinkAddButton.tsx index dc819ef38..4ad81a46f 100644 --- a/src/app/(main)/links/LinkAddButton.tsx +++ b/src/app/(main)/links/LinkAddButton.tsx @@ -1,17 +1,10 @@ -import { useMessages, useModified } from '@/components/hooks'; -import { Button, Icon, Modal, Dialog, DialogTrigger, Text, useToast } from '@umami/react-zen'; +import { useMessages } from '@/components/hooks'; +import { Button, Icon, Modal, Dialog, DialogTrigger, Text } from '@umami/react-zen'; import { Plus } from '@/components/icons'; import { LinkEditForm } from './LinkEditForm'; export function LinkAddButton({ teamId }: { teamId?: string }) { - const { formatMessage, labels, messages } = useMessages(); - const { toast } = useToast(); - const { touch } = useModified(); - - const handleSave = async () => { - toast(formatMessage(messages.saved)); - touch('links'); - }; + const { formatMessage, labels } = useMessages(); return ( @@ -23,7 +16,7 @@ export function LinkAddButton({ teamId }: { teamId?: string }) { - {({ close }) => } + {({ close }) => } diff --git a/src/app/(main)/pixels/PixelAddButton.tsx b/src/app/(main)/pixels/PixelAddButton.tsx index 0958ff0e6..9c60db8c6 100644 --- a/src/app/(main)/pixels/PixelAddButton.tsx +++ b/src/app/(main)/pixels/PixelAddButton.tsx @@ -1,17 +1,10 @@ -import { useMessages, useModified } from '@/components/hooks'; -import { Button, Icon, Modal, Dialog, DialogTrigger, Text, useToast } from '@umami/react-zen'; +import { useMessages } from '@/components/hooks'; +import { Button, Icon, Modal, Dialog, DialogTrigger, Text } from '@umami/react-zen'; import { Plus } from '@/components/icons'; import { PixelEditForm } from './PixelEditForm'; export function PixelAddButton({ teamId }: { teamId?: string }) { - const { formatMessage, labels, messages } = useMessages(); - const { toast } = useToast(); - const { touch } = useModified(); - - const handleSave = async () => { - toast(formatMessage(messages.saved)); - touch('pixels'); - }; + const { formatMessage, labels } = useMessages(); return ( @@ -23,7 +16,7 @@ export function PixelAddButton({ teamId }: { teamId?: string }) { - {({ close }) => } + {({ close }) => } From d8b3c8d13cab12eb62e8ac52ab65bf3bd0e3f428 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 22 Sep 2025 14:19:57 -0700 Subject: [PATCH 4/4] Pass headers for links and pixels. --- src/app/(collect)/p/[slug]/route.ts | 2 +- src/app/(collect)/q/[slug]/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(collect)/p/[slug]/route.ts b/src/app/(collect)/p/[slug]/route.ts index 97d9a3f2a..23ac03cc1 100644 --- a/src/app/(collect)/p/[slug]/route.ts +++ b/src/app/(collect)/p/[slug]/route.ts @@ -29,7 +29,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ slug const req = new Request(request.url, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: request.headers, body: JSON.stringify(payload), }); diff --git a/src/app/(collect)/q/[slug]/route.ts b/src/app/(collect)/q/[slug]/route.ts index 4c0f683c3..3a6806565 100644 --- a/src/app/(collect)/q/[slug]/route.ts +++ b/src/app/(collect)/q/[slug]/route.ts @@ -27,7 +27,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ slug const req = new Request(request.url, { method: 'POST', - headers: { 'Content-Type': 'application/json' }, + headers: request.headers, body: JSON.stringify(payload), });