Compare commits

...

33 Commits

Author SHA1 Message Date
81c43e01d3 fix: test 2024-03-07 12:38:02 +08:00
ffcb4fda20 fix: remove invalid seed 2024-03-07 12:12:24 +08:00
530508f421 fix: remove invalid seed 2024-03-07 12:09:14 +08:00
e748b1898e fix: add direct neon connection URL 2024-03-07 11:50:16 +08:00
88ffdb0066 fix: remove duplicate neon pooler 2024-03-06 16:36:48 +08:00
70494fa5bb feat: add offline development support (#987)
## Description

Add support to develop without network access since TRPC by default will
prevent network requests when offline.

https://tanstack.com/query/v4/docs/framework/react/guides/network-mode#network-mode-always

## Changes Made

- Add dynamic logic to toggle offline development
- Removed teams feature flag
2024-03-05 22:06:48 +11:00
f6f9c301da feat(ci): cache github workflow actions (#804) 2024-03-05 10:04:06 +11:00
6065140715 feat: cache layers 2024-01-06 15:29:19 +05:30
34a59d2db3 fix: cache 2024-01-06 15:18:33 +05:30
ba37633ecd fix: revert 2024-01-06 15:18:09 +05:30
46e83d65bb feat: cache docker 2024-01-06 15:14:28 +05:30
142c93aa63 chore: revert force build error 2024-01-06 14:45:44 +05:30
3eb1a17d3c chore: force build error 2024-01-06 14:42:49 +05:30
6d1ad179d4 feat: add clean cache workflow 2024-01-06 13:30:21 +05:30
8eed13e275 fix: remove additional workflow 2024-01-05 02:22:42 +05:30
346078dbbe fix: e2e 2024-01-05 02:15:27 +05:30
c8337d7dcc fix: key 2024-01-05 02:13:42 +05:30
75630ef19d fix: npm action 2024-01-05 01:58:00 +05:30
634807328e fix: command 2024-01-05 01:38:14 +05:30
2bbbe1098a fix: action 2024-01-05 01:32:47 +05:30
d24b9de254 fix: skip install 2024-01-05 01:19:22 +05:30
c86f79dd7b feat: add workflow call actions 2024-01-05 01:11:28 +05:30
0c12e34c38 fix: remove call 2024-01-05 01:08:32 +05:30
26b604dbd0 fix: add workflow call 2024-01-05 00:21:05 +05:30
308f55f3d4 fix: key 2024-01-05 00:09:41 +05:30
e5b7bf81fa fix: add action to codeql 2024-01-05 00:06:16 +05:30
b35f050409 fix: add shell 2024-01-05 00:03:20 +05:30
ce6f523230 fix: key 2024-01-04 23:56:32 +05:30
9e57de512a feat: use actions 2024-01-04 23:46:09 +05:30
9b5d64cc1a feat: add playwright action 2024-01-04 23:44:27 +05:30
fc372d0aa9 feat: add node install action 2024-01-04 23:41:48 +05:30
e470020b16 feat: add cache build action 2024-01-04 23:41:24 +05:30
0a9006430f fix: command 2024-01-04 23:40:35 +05:30
20 changed files with 239 additions and 306 deletions

24
.github/actions/cache-build/action.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Cache production build binaries
description: 'Cache or restore if necessary'
inputs:
node_version:
required: false
default: v18.x
runs:
using: 'composite'
steps:
- name: Cache production build
uses: actions/cache@v3
id: production-build-cache
with:
path: |
${{ github.workspace }}/apps/web/.next
${{ github.workspace }}/apps/marketing/.next
**/.turbo/**
**/dist/**
key: prod-build-${{ github.run_id }}
restore-keys: prod-build-
- run: npm run build
shell: bash

39
.github/actions/node-install/action.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: 'Setup node and cache node_modules'
inputs:
node_version:
required: false
default: v18.x
runs:
using: 'composite'
steps:
- name: Set up Node ${{ inputs.node_version }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Cache npm
uses: actions/cache@v3
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
- name: Cache node_modules
uses: actions/cache@v3
id: cache-node-modules
with:
path: |
node_modules
packages/*/node_modules
apps/*/node_modules
key: modules-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
shell: bash
run: |
npm ci --no-audit
npm run prisma:generate
env:
HUSKY: '0'

View File

@ -0,0 +1,19 @@
name: Install playwright binaries
description: 'Install playwright, cache and restore if necessary'
runs:
using: 'composite'
steps:
- name: Cache playwright
id: cache-playwright
uses: actions/cache@v3
with:
path: |
~/.cache/ms-playwright
${{ github.workspace }}/node_modules/playwright
key: playwright-${{ hashFiles('**/package-lock.json') }}
restore-keys: playwright-
- name: Install playwright
if: steps.cache-playwright.outputs.cache-hit != 'true'
run: npx playwright install --with-deps
shell: bash

View File

@ -1,6 +1,7 @@
name: 'Continuous Integration' name: 'Continuous Integration'
on: on:
workflow_call:
push: push:
branches: ['main'] branches: ['main']
pull_request: pull_request:
@ -10,9 +11,6 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true cancel-in-progress: true
env:
HUSKY: 0
jobs: jobs:
build_app: build_app:
name: Build App name: Build App
@ -23,20 +21,12 @@ jobs:
with: with:
fetch-depth: 2 fetch-depth: 2
- name: Install Node.js - uses: ./.github/actions/node-install
uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
- name: Install dependencies
run: npm ci
- name: Copy env - name: Copy env
run: cp .env.example .env run: cp .env.example .env
- name: Build - uses: ./.github/actions/cache-build
run: npm run build
build_docker: build_docker:
name: Build Docker Image name: Build Docker Image
@ -47,5 +37,31 @@ jobs:
with: with:
fetch-depth: 2 fetch-depth: 2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Build Docker Image - name: Build Docker Image
run: ./docker/build.sh uses: docker/build-push-action@v5
with:
push: false
context: .
file: ./docker/Dockerfile
tags: documenso-${{ github.sha }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- # Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

29
.github/workflows/clean-cache.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: cleanup caches by a branch
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge

View File

@ -25,19 +25,12 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
- name: Install Dependencies
run: npm ci
- name: Copy env - name: Copy env
run: cp .env.example .env run: cp .env.example .env
- name: Build Documenso - uses: ./.github/actions/node-install
run: npm run build
- uses: ./.github/actions/cache-build
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v2

View File

@ -6,29 +6,21 @@ on:
branches: ['main'] branches: ['main']
jobs: jobs:
e2e_tests: e2e_tests:
name: "E2E Tests" name: 'E2E Tests'
timeout-minutes: 60 timeout-minutes: 60
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
- name: Install dependencies
run: npm ci
- name: Copy env - name: Copy env
run: cp .env.example .env run: cp .env.example .env
- uses: ./.github/actions/node-install
- name: Start Services - name: Start Services
run: npm run dx:up run: npm run dx:up
- name: Install Playwright Browsers - uses: ./.github/actions/playwright-install
run: npx playwright install --with-deps
- name: Generate Prisma Client
run: npm run prisma:generate -w @documenso/prisma
- name: Create the database - name: Create the database
run: npm run prisma:migrate-dev run: npm run prisma:migrate-dev
@ -36,6 +28,8 @@ jobs:
- name: Seed the database - name: Seed the database
run: npm run prisma:seed run: npm run prisma:seed
- uses: ./.github/actions/cache-build
- name: Run Playwright tests - name: Run Playwright tests
run: npm run ci run: npm run ci
@ -43,7 +37,7 @@ jobs:
if: always() if: always()
with: with:
name: test-results name: test-results
path: "packages/app-tests/**/test-results/*" path: 'packages/app-tests/**/test-results/*'
retention-days: 30 retention-days: 30
env: env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

View File

@ -7,7 +7,6 @@ import { useParams } from 'next/navigation';
import { MenuIcon, SearchIcon } from 'lucide-react'; import { MenuIcon, SearchIcon } from 'lucide-react';
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
import type { GetTeamsResponse } from '@documenso/lib/server-only/team/get-teams'; import type { GetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
import { getRootHref } from '@documenso/lib/utils/params'; import { getRootHref } from '@documenso/lib/utils/params';
import type { User } from '@documenso/prisma/client'; import type { User } from '@documenso/prisma/client';
@ -19,7 +18,6 @@ import { CommandMenu } from '../common/command-menu';
import { DesktopNav } from './desktop-nav'; import { DesktopNav } from './desktop-nav';
import { MenuSwitcher } from './menu-switcher'; import { MenuSwitcher } from './menu-switcher';
import { MobileNavigation } from './mobile-navigation'; import { MobileNavigation } from './mobile-navigation';
import { ProfileDropdown } from './profile-dropdown';
export type HeaderProps = HTMLAttributes<HTMLDivElement> & { export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
user: User; user: User;
@ -29,10 +27,6 @@ export type HeaderProps = HTMLAttributes<HTMLDivElement> & {
export const Header = ({ className, user, teams, ...props }: HeaderProps) => { export const Header = ({ className, user, teams, ...props }: HeaderProps) => {
const params = useParams(); const params = useParams();
const { getFlag } = useFeatureFlags();
const isTeamsEnabled = getFlag('app_teams');
const [isCommandMenuOpen, setIsCommandMenuOpen] = useState(false); const [isCommandMenuOpen, setIsCommandMenuOpen] = useState(false);
const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false); const [isHamburgerMenuOpen, setIsHamburgerMenuOpen] = useState(false);
const [scrollY, setScrollY] = useState(0); const [scrollY, setScrollY] = useState(0);
@ -47,34 +41,6 @@ export const Header = ({ className, user, teams, ...props }: HeaderProps) => {
return () => window.removeEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll);
}, []); }, []);
if (!isTeamsEnabled) {
return (
<header
className={cn(
'supports-backdrop-blur:bg-background/60 bg-background/95 sticky top-0 z-[60] flex h-16 w-full items-center border-b border-b-transparent backdrop-blur duration-200',
scrollY > 5 && 'border-b-border',
className,
)}
{...props}
>
<div className="mx-auto flex w-full max-w-screen-xl items-center justify-between gap-x-4 px-4 md:justify-normal md:px-8">
<Link
href="/"
className="focus-visible:ring-ring ring-offset-background rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Logo className="h-6 w-auto" />
</Link>
<DesktopNav />
<div className="flex gap-x-4 md:ml-8">
<ProfileDropdown user={user} />
</div>
</div>
</header>
);
}
return ( return (
<header <header
className={cn( className={cn(

View File

@ -1,177 +0,0 @@
'use client';
import Link from 'next/link';
import {
Braces,
CreditCard,
FileSpreadsheet,
Lock,
LogOut,
User as LucideUser,
Monitor,
Moon,
Palette,
Sun,
UserCog,
} from 'lucide-react';
import { signOut } from 'next-auth/react';
import { useTheme } from 'next-themes';
import { LuGithub } from 'react-icons/lu';
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import type { User } from '@documenso/prisma/client';
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
import { Button } from '@documenso/ui/primitives/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@documenso/ui/primitives/dropdown-menu';
export type ProfileDropdownProps = {
user: User;
};
export const ProfileDropdown = ({ user }: ProfileDropdownProps) => {
const { getFlag } = useFeatureFlags();
const { theme, setTheme } = useTheme();
const isUserAdmin = isAdmin(user);
const isBillingEnabled = getFlag('app_billing');
const avatarFallback = user.name
? extractInitials(user.name)
: user.email.slice(0, 1).toUpperCase();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
title="Profile Dropdown"
className="relative h-10 w-10 rounded-full"
>
<Avatar className="h-10 w-10">
<AvatarFallback>{avatarFallback}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="z-[60] w-56" align="end" forceMount>
<DropdownMenuLabel>Account</DropdownMenuLabel>
{isUserAdmin && (
<>
<DropdownMenuItem asChild>
<Link href="/admin" className="cursor-pointer">
<UserCog className="mr-2 h-4 w-4" />
Admin
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
)}
<DropdownMenuItem asChild>
<Link href="/settings/profile" className="cursor-pointer">
<LucideUser className="mr-2 h-4 w-4" />
Profile
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/settings/security" className="cursor-pointer">
<Lock className="mr-2 h-4 w-4" />
Security
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/settings/tokens" className="cursor-pointer">
<Braces className="mr-2 h-4 w-4" />
API Tokens
</Link>
</DropdownMenuItem>
{isBillingEnabled && (
<DropdownMenuItem asChild>
<Link href="/settings/billing" className="cursor-pointer">
<CreditCard className="mr-2 h-4 w-4" />
Billing
</Link>
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/templates" className="cursor-pointer">
<FileSpreadsheet className="mr-2 h-4 w-4" />
Templates
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Palette className="mr-2 h-4 w-4" />
Themes
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent className="z-[60]">
<DropdownMenuRadioGroup value={theme} onValueChange={setTheme}>
<DropdownMenuRadioItem value="light">
<Sun className="mr-2 h-4 w-4" /> Light
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="dark">
<Moon className="mr-2 h-4 w-4" />
Dark
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="system">
<Monitor className="mr-2 h-4 w-4" />
System
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href="https://github.com/documenso/documenso"
className="cursor-pointer"
target="_blank"
>
<LuGithub className="mr-2 h-4 w-4" />
Star on Github
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() =>
void signOut({
callbackUrl: '/',
})
}
>
<LogOut className="mr-2 h-4 w-4" />
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};

View File

@ -19,7 +19,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
const { getFlag } = useFeatureFlags(); const { getFlag } = useFeatureFlags();
const isBillingEnabled = getFlag('app_billing'); const isBillingEnabled = getFlag('app_billing');
const isTeamsEnabled = getFlag('app_teams');
return ( return (
<div className={cn('flex flex-col gap-y-2', className)} {...props}> <div className={cn('flex flex-col gap-y-2', className)} {...props}>
@ -36,20 +35,18 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
</Button> </Button>
</Link> </Link>
{isTeamsEnabled && ( <Link href="/settings/teams">
<Link href="/settings/teams"> <Button
<Button variant="ghost"
variant="ghost" className={cn(
className={cn( 'w-full justify-start',
'w-full justify-start', pathname?.startsWith('/settings/teams') && 'bg-secondary',
pathname?.startsWith('/settings/teams') && 'bg-secondary', )}
)} >
> <Users className="mr-2 h-5 w-5" />
<Users className="mr-2 h-5 w-5" /> Teams
Teams </Button>
</Button> </Link>
</Link>
)}
<Link href="/settings/security"> <Link href="/settings/security">
<Button <Button

View File

@ -19,7 +19,6 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
const { getFlag } = useFeatureFlags(); const { getFlag } = useFeatureFlags();
const isBillingEnabled = getFlag('app_billing'); const isBillingEnabled = getFlag('app_billing');
const isTeamsEnabled = getFlag('app_teams');
return ( return (
<div <div
@ -39,20 +38,18 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
</Button> </Button>
</Link> </Link>
{isTeamsEnabled && ( <Link href="/settings/teams">
<Link href="/settings/teams"> <Button
<Button variant="ghost"
variant="ghost" className={cn(
className={cn( 'w-full justify-start',
'w-full justify-start', pathname?.startsWith('/settings/teams') && 'bg-secondary',
pathname?.startsWith('/settings/teams') && 'bg-secondary', )}
)} >
> <Users className="mr-2 h-5 w-5" />
<Users className="mr-2 h-5 w-5" /> Teams
Teams </Button>
</Button> </Link>
</Link>
)}
<Link href="/settings/security"> <Link href="/settings/security">
<Button <Button

View File

@ -15,7 +15,7 @@
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev", "dx": "npm i && npm run dx:up && npm run prisma:migrate-dev",
"dx:up": "docker compose -f docker/compose-services.yml up -d", "dx:up": "docker compose -f docker/compose-services.yml up -d",
"dx:down": "docker compose -f docker/compose-services.yml down", "dx:down": "docker compose -f docker/compose-services.yml down",
"ci": "turbo run build test:e2e", "ci": "turbo run test:e2e",
"prisma:generate": "npm run with:env -- npm run prisma:generate -w @documenso/prisma", "prisma:generate": "npm run with:env -- npm run prisma:generate -w @documenso/prisma",
"prisma:migrate-dev": "npm run with:env -- npm run prisma:migrate-dev -w @documenso/prisma", "prisma:migrate-dev": "npm run with:env -- npm run prisma:migrate-dev -w @documenso/prisma",
"prisma:migrate-deploy": "npm run with:env -- npm run prisma:migrate-deploy -w @documenso/prisma", "prisma:migrate-deploy": "npm run with:env -- npm run prisma:migrate-deploy -w @documenso/prisma",

View File

@ -22,7 +22,6 @@ export const FEATURE_FLAG_POLL_INTERVAL = 30000;
*/ */
export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = { export const LOCAL_FEATURE_FLAGS: Record<string, boolean> = {
app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true', app_billing: NEXT_PUBLIC_FEATURE_BILLING_ENABLED() === 'true',
app_teams: true,
app_document_page_view_history_sheet: false, app_document_page_view_history_sheet: false,
marketing_header_single_player_mode: false, marketing_header_single_player_mode: false,
marketing_profiles_announcement_bar: true, marketing_profiles_announcement_bar: true,

View File

@ -15,6 +15,10 @@ export const getDatabaseUrl = () => {
process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.DATABASE_URL; process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.DATABASE_URL;
} }
if (process.env.DATABASE_URL_UNPOOLED) {
process.env.NEXT_PRIVATE_DIRECT_DATABASE_URL = process.env.DATABASE_URL_UNPOOLED;
}
if (process.env.POSTGRES_PRISMA_URL) { if (process.env.POSTGRES_PRISMA_URL) {
process.env.NEXT_PRIVATE_DATABASE_URL = process.env.POSTGRES_PRISMA_URL; process.env.NEXT_PRIVATE_DATABASE_URL = process.env.POSTGRES_PRISMA_URL;
} }
@ -40,18 +44,5 @@ export const getDatabaseUrl = () => {
process.env.NEXT_PRIVATE_DATABASE_URL = url.toString().replace('https://', 'postgres://'); process.env.NEXT_PRIVATE_DATABASE_URL = url.toString().replace('https://', 'postgres://');
} }
// Support for neon.tech (Neon Database)
if (url.hostname.endsWith('neon.tech')) {
const [projectId, ...rest] = url.hostname.split('.');
if (!projectId.endsWith('-pooler')) {
url.hostname = `${projectId}-pooler.${rest.join('.')}`;
}
url.searchParams.set('pgbouncer', 'true');
process.env.NEXT_PRIVATE_DATABASE_URL = url.toString().replace('https://', 'postgres://');
}
return process.env.NEXT_PRIVATE_DATABASE_URL; return process.env.NEXT_PRIVATE_DATABASE_URL;
}; };

View File

@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "DummyData" (
"id" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
"hello" TEXT,
CONSTRAINT "DummyData_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "DummyData" ADD CONSTRAINT "DummyData_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -52,10 +52,19 @@ model User {
securityAuditLogs UserSecurityAuditLog[] securityAuditLogs UserSecurityAuditLog[]
Webhooks Webhook[] Webhooks Webhook[]
siteSettings SiteSettings[] siteSettings SiteSettings[]
dummyData DummyData[]
@@index([email]) @@index([email])
} }
model DummyData {
id String @id @default(cuid())
userId Int
hello String?
User User? @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model UserProfile { model UserProfile {
id Int @id id Int @id
bio String? bio String?

View File

@ -13,7 +13,13 @@ const seedDatabase = async () => {
if ('seedDatabase' in mod && typeof mod.seedDatabase === 'function') { if ('seedDatabase' in mod && typeof mod.seedDatabase === 'function') {
console.log(`[SEEDING]: ${file}`); console.log(`[SEEDING]: ${file}`);
await mod.seedDatabase();
try {
await mod.seedDatabase();
} catch (e) {
console.log(`[SEEDING]: Seed failed for ${file}`);
console.error(e);
}
} }
} }
} }

View File

@ -2,6 +2,7 @@
import { useState } from 'react'; import { useState } from 'react';
import type { QueryClientConfig } from '@tanstack/react-query';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client'; import { httpBatchLink } from '@trpc/client';
import { createTRPCReact } from '@trpc/react-query'; import { createTRPCReact } from '@trpc/react-query';
@ -27,7 +28,27 @@ export interface TrpcProviderProps {
} }
export function TrpcProvider({ children }: TrpcProviderProps) { export function TrpcProvider({ children }: TrpcProviderProps) {
const [queryClient] = useState(() => new QueryClient()); let queryClientConfig: QueryClientConfig | undefined;
const isDevelopingOffline =
typeof window !== 'undefined' &&
window.location.hostname === 'localhost' &&
!window.navigator.onLine;
if (isDevelopingOffline) {
queryClientConfig = {
defaultOptions: {
queries: {
networkMode: 'always',
},
mutations: {
networkMode: 'always',
},
},
};
}
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
const [trpcClient] = useState(() => const [trpcClient] = useState(() =>
trpc.createClient({ trpc.createClient({

View File

@ -6,7 +6,6 @@ set -eo pipefail
# Get the directory of this script, regardless of where it is called from. # Get the directory of this script, regardless of where it is called from.
SCRIPT_DIR="$(readlink -f "$(dirname "$0")")" SCRIPT_DIR="$(readlink -f "$(dirname "$0")")"
function log() { function log() {
echo "[VercelBuild]: $1" echo "[VercelBuild]: $1"
} }
@ -69,19 +68,17 @@ function remap_database_integration() {
export NEXT_PRIVATE_DIRECT_DATABASE_URL="$DATABASE_URL" export NEXT_PRIVATE_DIRECT_DATABASE_URL="$DATABASE_URL"
fi fi
if [[ ! -z "$DATABASE_URL_UNPOOLED" ]]; then
log "Remapping for Neon integration"
export NEXT_PRIVATE_DATABASE_URL="$DATABASE_URL&pgbouncer=true"
export NEXT_PRIVATE_DIRECT_DATABASE_URL="$DATABASE_URL_UNPOOLED"
fi
if [[ ! -z "$POSTGRES_URL_NON_POOLING" ]]; then if [[ ! -z "$POSTGRES_URL_NON_POOLING" ]]; then
export NEXT_PRIVATE_DATABASE_URL="$POSTGRES_URL?pgbouncer=true" export NEXT_PRIVATE_DATABASE_URL="$POSTGRES_URL?pgbouncer=true"
export NEXT_PRIVATE_DIRECT_DATABASE_URL="$POSTGRES_URL_NON_POOLING" export NEXT_PRIVATE_DIRECT_DATABASE_URL="$POSTGRES_URL_NON_POOLING"
fi fi
if [[ "$NEXT_PRIVATE_DATABASE_URL" == *"neon.tech"* ]]; then
log "Remapping for Neon integration"
PROJECT_ID="$(echo "$PGHOST" | cut -d'.' -f1)"
PGBOUNCER_HOST="$(echo "$PGHOST" | sed "s/${PROJECT_ID}/${PROJECT_ID}-pooler/")"
export NEXT_PRIVATE_DATABASE_URL="postgres://${PGUSER}:${PGPASSWORD}@${PGBOUNCER_HOST}/${PGDATABASE}?pgbouncer=true"
fi
} }
# Navigate to the root of the project. # Navigate to the root of the project.

View File

@ -27,7 +27,8 @@
"cache": false "cache": false
}, },
"test:e2e": { "test:e2e": {
"dependsOn": ["^build"] "dependsOn": ["^build"],
"cache": false
} }
}, },
"globalDependencies": ["**/.env.*local"], "globalDependencies": ["**/.env.*local"],
@ -89,6 +90,7 @@
"FONT_CAVEAT_URI", "FONT_CAVEAT_URI",
"POSTGRES_URL", "POSTGRES_URL",
"DATABASE_URL", "DATABASE_URL",
"DATABASE_URL_UNPOOLED",
"POSTGRES_PRISMA_URL", "POSTGRES_PRISMA_URL",
"POSTGRES_URL_NON_POOLING", "POSTGRES_URL_NON_POOLING",
"E2E_TEST_AUTHENTICATE_USERNAME", "E2E_TEST_AUTHENTICATE_USERNAME",