Skip to content

Commit b039e6a

Browse files
committed
Merge remote-tracking branch 'origin/dev' into custom-dashboards-and-unified-ai-no-playground
2 parents 9ee952b + a2e2de0 commit b039e6a

202 files changed

Lines changed: 34490 additions & 12562 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docker-emulator-test.yaml

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
name: Runs E2E API Tests (Local Emulator)
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- dev
8+
pull_request:
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/dev' }}
13+
14+
jobs:
15+
build:
16+
name: E2E Tests (Local Emulator, Node ${{ matrix.node-version }})
17+
runs-on: ubicloud-standard-8
18+
env:
19+
NODE_ENV: test
20+
NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR: "true"
21+
STACK_ENABLE_HARDCODED_PASSKEY_CHALLENGE_FOR_TESTING: yes
22+
STACK_DATABASE_CONNECTION_STRING: "postgres://postgres:PASSWORD-PLACEHOLDER--uqfEC1hmmv@localhost:8128/stackframe"
23+
STACK_EXTERNAL_DB_SYNC_MAX_DURATION_MS: "20000"
24+
STACK_EXTERNAL_DB_SYNC_DIRECT: "false"
25+
26+
strategy:
27+
matrix:
28+
node-version: [22.x]
29+
30+
steps:
31+
- uses: actions/checkout@v6
32+
33+
- name: Setup Node.js ${{ matrix.node-version }}
34+
uses: actions/setup-node@v6
35+
with:
36+
node-version: ${{ matrix.node-version }}
37+
38+
- name: Setup pnpm
39+
uses: pnpm/action-setup@v4
40+
41+
# Even just starting the Docker Compose as a daemon is slow because we have to download and build the images
42+
# so, we run it in the background
43+
- name: Start Docker Compose in background
44+
uses: JarvusInnovations/background-action@v1.0.7
45+
with:
46+
run: docker compose -f docker/dependencies/docker.compose.yaml up --pull always -d &
47+
# we don't need to wait on anything, just need to start the daemon
48+
wait-on: /dev/null
49+
tail: true
50+
wait-for: 3s
51+
log-output-if: true
52+
53+
- name: Install dependencies
54+
run: pnpm install --frozen-lockfile
55+
56+
- name: Create .env.test.local file for apps/backend
57+
run: cp apps/backend/.env.development apps/backend/.env.test.local
58+
59+
- name: Create .env.test.local file for apps/dashboard
60+
run: cp apps/dashboard/.env.development apps/dashboard/.env.test.local
61+
62+
- name: Create .env.test.local file for apps/e2e
63+
run: cp apps/e2e/.env.development apps/e2e/.env.test.local
64+
65+
- name: Create .env.test.local file for docs
66+
run: cp docs/.env.development docs/.env.test.local
67+
68+
- name: Create .env.test.local file for examples/cjs-test
69+
run: cp examples/cjs-test/.env.development examples/cjs-test/.env.test.local
70+
71+
- name: Create .env.test.local file for examples/demo
72+
run: cp examples/demo/.env.development examples/demo/.env.test.local
73+
74+
- name: Create .env.test.local file for examples/docs-examples
75+
run: cp examples/docs-examples/.env.development examples/docs-examples/.env.test.local
76+
77+
- name: Create .env.test.local file for examples/e-commerce
78+
run: cp examples/e-commerce/.env.development examples/e-commerce/.env.test.local
79+
80+
- name: Create .env.test.local file for examples/middleware
81+
run: cp examples/middleware/.env.development examples/middleware/.env.test.local
82+
83+
- name: Create .env.test.local file for examples/supabase
84+
run: cp examples/supabase/.env.development examples/supabase/.env.test.local
85+
86+
- name: Create .env.test.local file for examples/convex
87+
run: cp examples/convex/.env.development examples/convex/.env.test.local
88+
89+
- name: Build
90+
run: pnpm build
91+
92+
- name: Wait on Postgres
93+
run: pnpm run wait-until-postgres-is-ready:pg_isready
94+
95+
- name: Wait on Inbucket
96+
run: pnpx wait-on tcp:localhost:8129
97+
98+
- name: Wait on Svix
99+
run: pnpx wait-on tcp:localhost:8113
100+
101+
- name: Wait on QStash
102+
run: pnpx wait-on tcp:localhost:8125
103+
104+
- name: Wait on ClickHouse
105+
run: pnpx wait-on http://localhost:8136/ping
106+
107+
- name: Initialize database
108+
run: pnpm run db:init
109+
110+
- name: Start stack-backend in background
111+
uses: JarvusInnovations/background-action@v1.0.7
112+
with:
113+
run: pnpm run start:backend --log-order=stream &
114+
wait-on: |
115+
http://localhost:8102
116+
tail: true
117+
wait-for: 30s
118+
log-output-if: true
119+
120+
- name: Start stack-dashboard in background
121+
uses: JarvusInnovations/background-action@v1.0.7
122+
with:
123+
run: pnpm run start:dashboard --log-order=stream &
124+
wait-on: |
125+
http://localhost:8101
126+
tail: true
127+
wait-for: 30s
128+
log-output-if: true
129+
130+
- name: Start mock-oauth-server in background
131+
uses: JarvusInnovations/background-action@v1.0.7
132+
with:
133+
run: pnpm run start:mock-oauth-server --log-order=stream &
134+
wait-on: |
135+
http://localhost:8102
136+
tail: true
137+
wait-for: 30s
138+
log-output-if: true
139+
140+
- name: Start run-email-queue in background
141+
uses: JarvusInnovations/background-action@v1.0.7
142+
with:
143+
run: pnpm -C apps/backend run run-email-queue --log-order=stream &
144+
wait-on: |
145+
http://localhost:8102
146+
tail: true
147+
wait-for: 30s
148+
log-output-if: true
149+
150+
- name: Start run-cron-jobs in background
151+
uses: JarvusInnovations/background-action@v1.0.7
152+
with:
153+
run: pnpm -C apps/backend run run-cron-jobs:test --log-order=stream &
154+
wait-on: |
155+
http://localhost:8102
156+
tail: true
157+
wait-for: 30s
158+
log-output-if: true
159+
160+
- name: Wait 10 seconds
161+
run: sleep 10
162+
163+
- name: Run tests
164+
run: pnpm test run
165+
166+
- name: Run tests again (attempt 1)
167+
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
168+
run: pnpm test run
169+
170+
- name: Run tests again (attempt 2)
171+
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
172+
run: pnpm test run
173+
174+
- name: Verify data integrity
175+
run: pnpm run verify-data-integrity --no-bail
176+
177+
- name: Print Docker Compose logs
178+
if: always()
179+
run: docker compose -f docker/dependencies/docker.compose.yaml logs

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ debug.log
1313

1414

1515
.vercel
16+
.output
17+
.nitro
1618

1719
# https://stackoverflow.com/questions/76510164/can-i-safely-delete-vite-config-ts-timestamp-files
1820
vite.config.ts.timestamp-*

apps/backend/.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Basic
22
NEXT_PUBLIC_STACK_API_URL=# the base URL of Stack's backend/API. For local development, this is `http://localhost:8102`; for the managed service, this is `https://api.stack-auth.com`.
33
NEXT_PUBLIC_STACK_DASHBOARD_URL=# the URL of Stack's dashboard. For local development, this is `http://localhost:8101`; for the managed service, this is `https://app.stack-auth.com`.
4+
NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR=# set to true to enable local emulator-only behaviors (internal local emulator endpoints, read-only environment config overrides, and local emulator auth UX)
45
STACK_SECRET_SERVER_KEY=# a random, unguessable secret key generated by `pnpm generate-keys`
56

67

apps/backend/.env.development

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
NEXT_PUBLIC_STACK_API_URL=http://localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}02
22
NEXT_PUBLIC_STACK_DASHBOARD_URL=http://localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}01
3+
NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR=false
34
STACK_SERVER_SECRET=23-wuNpik0gIW4mruTz25rbIvhuuvZFrLOLtL7J4tyo
45

56
STACK_CHANGELOG_URL=https://raw.githubusercontent.com/stack-auth/stack-auth/refs/heads/dev/CHANGELOG.md
@@ -93,3 +94,10 @@ STACK_CLICKHOUSE_URL=http://localhost:${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}36
9394
STACK_CLICKHOUSE_ADMIN_USER=stackframe
9495
STACK_CLICKHOUSE_ADMIN_PASSWORD=PASSWORD-PLACEHOLDER--9gKyMxJeMx
9596
STACK_CLICKHOUSE_EXTERNAL_PASSWORD=PASSWORD-PLACEHOLDER--EZeHscBMzE
97+
98+
# Managed emails
99+
STACK_RESEND_API_KEY=mock_resend_api_key
100+
STACK_RESEND_WEBHOOK_SECRET=mock_resend_webhook_secret
101+
STACK_DNSIMPLE_API_TOKEN=mock_dnsimple_api_token
102+
STACK_DNSIMPLE_ACCOUNT_ID=mock_dnsimple_account_id
103+
STACK_DNSIMPLE_API_BASE_URL=https://api.dnsimple.com/v2

apps/backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stackframe/backend",
3-
"version": "2.8.71",
3+
"version": "2.8.72",
44
"repository": "https://github.com/stack-auth/stack-auth",
55
"private": true,
66
"type": "module",
@@ -92,6 +92,7 @@
9292
"dotenv": "^16.4.5",
9393
"dotenv-cli": "^7.3.0",
9494
"freestyle-sandboxes": "^0.1.6",
95+
"jiti": "^2.6.1",
9596
"jose": "^6.1.3",
9697
"json-diff": "^1.0.6",
9798
"next": "16.1.5",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- AlterTable
2+
ALTER TABLE "Tenancy" ADD COLUMN "emailCapacityBoostExpiresAt" TIMESTAMP(3);
3+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
CREATE TYPE "ManagedEmailDomainStatus" AS ENUM ('PENDING_DNS', 'PENDING_VERIFICATION', 'VERIFIED', 'APPLIED', 'FAILED');
2+
3+
CREATE TABLE "ManagedEmailDomain" (
4+
"id" UUID NOT NULL,
5+
"tenancyId" UUID NOT NULL,
6+
"projectId" TEXT NOT NULL,
7+
"branchId" TEXT NOT NULL,
8+
"subdomain" TEXT NOT NULL,
9+
"senderLocalPart" TEXT NOT NULL,
10+
"resendDomainId" TEXT NOT NULL,
11+
"nameServerRecords" JSONB NOT NULL,
12+
"status" "ManagedEmailDomainStatus" NOT NULL DEFAULT 'PENDING_VERIFICATION',
13+
"providerStatusRaw" TEXT,
14+
"isActive" BOOLEAN NOT NULL DEFAULT true,
15+
"lastError" TEXT,
16+
"verifiedAt" TIMESTAMP(3),
17+
"appliedAt" TIMESTAMP(3),
18+
"lastWebhookAt" TIMESTAMP(3),
19+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
20+
"updatedAt" TIMESTAMP(3) NOT NULL,
21+
22+
CONSTRAINT "ManagedEmailDomain_pkey" PRIMARY KEY ("id"),
23+
CONSTRAINT "ManagedEmailDomain_tenancyId_fkey" FOREIGN KEY ("tenancyId") REFERENCES "Tenancy"("id") ON DELETE CASCADE ON UPDATE CASCADE
24+
);
25+
26+
CREATE UNIQUE INDEX "ManagedEmailDomain_resendDomainId_key" ON "ManagedEmailDomain"("resendDomainId");
27+
CREATE UNIQUE INDEX "ManagedEmailDomain_tenancyId_subdomain_key" ON "ManagedEmailDomain"("tenancyId", "subdomain");
28+
CREATE INDEX "ManagedEmailDomain_tenancy_status_active_idx" ON "ManagedEmailDomain"("tenancyId", "status", "isActive");
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CREATE TABLE "LocalEmulatorProject" (
2+
"absoluteFilePath" TEXT NOT NULL,
3+
"projectId" TEXT NOT NULL,
4+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
5+
"updatedAt" TIMESTAMP(3) NOT NULL,
6+
7+
CONSTRAINT "LocalEmulatorProject_pkey" PRIMARY KEY ("absoluteFilePath")
8+
);
9+
10+
CREATE UNIQUE INDEX "LocalEmulatorProject_projectId_key" ON "LocalEmulatorProject"("projectId");
11+
12+
ALTER TABLE "LocalEmulatorProject"
13+
ADD CONSTRAINT "LocalEmulatorProject_projectId_fkey"
14+
FOREIGN KEY ("projectId")
15+
REFERENCES "Project"("id")
16+
ON DELETE CASCADE
17+
ON UPDATE CASCADE;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { randomUUID } from "crypto";
2+
import type { Sql } from "postgres";
3+
import { expect } from "vitest";
4+
5+
export const preMigration = async (sql: Sql) => {
6+
const projectId = `test-${randomUUID()}`;
7+
await sql`
8+
INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode")
9+
VALUES (${projectId}, NOW(), NOW(), 'Cascade Test Project', '', false)
10+
`;
11+
return { projectId };
12+
};
13+
14+
export const postMigration = async (sql: Sql, ctx: Awaited<ReturnType<typeof preMigration>>) => {
15+
const absoluteFilePath = `/tmp/${randomUUID()}/stack.config.ts`;
16+
17+
await sql`
18+
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
19+
VALUES (${absoluteFilePath}, ${ctx.projectId}, NOW(), NOW())
20+
`;
21+
22+
await sql`DELETE FROM "Project" WHERE "id" = ${ctx.projectId}`;
23+
24+
const rows = await sql`
25+
SELECT "absoluteFilePath"
26+
FROM "LocalEmulatorProject"
27+
WHERE "absoluteFilePath" = ${absoluteFilePath}
28+
`;
29+
expect(rows).toHaveLength(0);
30+
};

0 commit comments

Comments
 (0)