Skip to content

Commit 7d42522

Browse files
authored
Merge branch 'fraud-protection' into fraud-protection-country-code
2 parents 9c5eda1 + 58aeb3c commit 7d42522

51 files changed

Lines changed: 1665 additions & 583 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: 1 addition & 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

apps/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"dotenv": "^16.4.5",
9191
"dotenv-cli": "^7.3.0",
9292
"freestyle-sandboxes": "^0.1.6",
93+
"jiti": "^2.6.1",
9394
"jose": "^6.1.3",
9495
"json-diff": "^1.0.6",
9596
"next": "16.1.5",
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+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 projectId1 = `test-${randomUUID()}`;
7+
const projectId2 = `test-${randomUUID()}`;
8+
9+
await sql`
10+
INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode")
11+
VALUES (${projectId1}, NOW(), NOW(), 'Local Emulator Project 1', '', false)
12+
`;
13+
await sql`
14+
INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode")
15+
VALUES (${projectId2}, NOW(), NOW(), 'Local Emulator Project 2', '', false)
16+
`;
17+
18+
return { projectId1, projectId2 };
19+
};
20+
21+
export const postMigration = async (sql: Sql, ctx: Awaited<ReturnType<typeof preMigration>>) => {
22+
const path1 = `/tmp/${randomUUID()}/stack.config.ts`;
23+
const path2 = `/tmp/${randomUUID()}/stack.config.ts`;
24+
25+
await sql`
26+
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
27+
VALUES (${path1}, ${ctx.projectId1}, NOW(), NOW())
28+
`;
29+
30+
await expect(sql`
31+
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
32+
VALUES (${path1}, ${ctx.projectId2}, NOW(), NOW())
33+
`).rejects.toThrow(/LocalEmulatorProject_pkey/);
34+
35+
await expect(sql`
36+
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
37+
VALUES (${path2}, ${ctx.projectId1}, NOW(), NOW())
38+
`).rejects.toThrow(/LocalEmulatorProject_projectId_key/);
39+
40+
await sql`
41+
INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt")
42+
VALUES (${path2}, ${ctx.projectId2}, NOW(), NOW())
43+
`;
44+
};

apps/backend/prisma/schema.prisma

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,21 @@ model Project {
4141
tenancies Tenancy[]
4242
branchConfigOverrides BranchConfigOverride[]
4343
environmentConfigOverrides EnvironmentConfigOverride[]
44+
localEmulatorProject LocalEmulatorProject?
4445
4546
@@index([ownerTeamId], map: "Project_ownerTeamId_idx")
4647
}
4748

49+
model LocalEmulatorProject {
50+
absoluteFilePath String @id
51+
projectId String @unique
52+
53+
createdAt DateTime @default(now())
54+
updatedAt DateTime @updatedAt
55+
56+
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
57+
}
58+
4859
model Tenancy {
4960
id String @id @default(uuid()) @db.Uuid
5061

0 commit comments

Comments
 (0)