mirror of
https://github.com/stack-auth/stack.git
synced 2026-07-20 21:29:36 +08:00
Merge branch 'stack-auth:dev' into feat/python-sdk
This commit is contained in:
commit
5c09ea70c3
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/backend",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -10,6 +10,10 @@ ALTER TABLE "ProjectUser"
|
||||
ADD COLUMN "signUpEmailNormalized" TEXT,
|
||||
ADD COLUMN "signUpEmailBase" TEXT;
|
||||
|
||||
-- Backward compatibility during rollout: old backends omit `signedUpAt` on
|
||||
-- INSERT, so set a default immediately in the first migration.
|
||||
ALTER TABLE "ProjectUser" ALTER COLUMN "signedUpAt" SET DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
-- Add the risk score bounds without validating existing rows yet.
|
||||
ALTER TABLE "ProjectUser"
|
||||
ADD CONSTRAINT "ProjectUser_risk_score_bot_range"
|
||||
|
||||
@ -28,13 +28,6 @@ ALTER TABLE "ProjectUser" VALIDATE CONSTRAINT "ProjectUser_risk_score_bot_range"
|
||||
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
|
||||
ALTER TABLE "ProjectUser" VALIDATE CONSTRAINT "ProjectUser_risk_score_free_trial_abuse_range";
|
||||
|
||||
-- Enforce `signedUpAt` after the backfill is complete. Set a DEFAULT first so
|
||||
-- that inserts omitting the column still get a sensible value (CURRENT_TIMESTAMP).
|
||||
-- SPLIT_STATEMENT_SENTINEL
|
||||
-- SINGLE_STATEMENT_SENTINEL
|
||||
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
|
||||
ALTER TABLE "ProjectUser" ALTER COLUMN "signedUpAt" SET DEFAULT CURRENT_TIMESTAMP;
|
||||
|
||||
-- SPLIT_STATEMENT_SENTINEL
|
||||
-- SINGLE_STATEMENT_SENTINEL
|
||||
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getEnvVariable, getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
|
||||
import { captureError, StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { traceSpan } from "@stackframe/stack-shared/dist/utils/telemetry";
|
||||
import createEmailableClient from "emailable";
|
||||
|
||||
@ -41,17 +42,15 @@ function validateVerifyResponse(value: unknown) {
|
||||
|
||||
async function verifyWithRetries(verifyFn: () => Promise<unknown>, maxAttempts: number, delayBaseMs: number) {
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
try {
|
||||
return await verifyFn();
|
||||
} catch (error) {
|
||||
const code = (error != null && typeof error === "object" && !Array.isArray(error))
|
||||
? Reflect.get(error, "code")
|
||||
: null;
|
||||
if (code !== 249) throw error; // only retry rate-limit errors
|
||||
if (i < maxAttempts - 1) {
|
||||
await new Promise(r => setTimeout(r, (Math.random() + 0.5) * delayBaseMs * (2 ** i)));
|
||||
const res: any = await verifyFn();
|
||||
if (!("state" in res)) {
|
||||
if ("message" in res && res.message.includes("Your request is taking longer than normal")) {
|
||||
await wait((Math.random() + 0.5) * delayBaseMs * (2 ** i));
|
||||
continue;
|
||||
}
|
||||
throw new StackAssertionError("Emailable returned an unexpected response body", { response: res });
|
||||
}
|
||||
return res;
|
||||
}
|
||||
throw new StackAssertionError("Timed out while verifying email address with Emailable");
|
||||
}
|
||||
@ -81,46 +80,46 @@ export async function checkEmailWithEmailable(
|
||||
_clientFactory?: (apiKey: string) => { verify: (email: string) => Promise<unknown> },
|
||||
},
|
||||
): Promise<EmailableCheckResult> {
|
||||
const rawApiKey = getEnvVariable("STACK_EMAILABLE_API_KEY", "");
|
||||
const emailDomain = email.split("@")[1]?.toLowerCase() ?? "";
|
||||
try {
|
||||
const rawApiKey = getEnvVariable("STACK_EMAILABLE_API_KEY", "");
|
||||
const emailDomain = email.split("@")[1]?.toLowerCase() ?? "";
|
||||
|
||||
// Always reject the explicit test domain, regardless of API key
|
||||
if (emailDomain === EMAILABLE_NOT_DELIVERABLE_TEST_DOMAIN) {
|
||||
const testResponse = buildTestUndeliverableResponse(email);
|
||||
return { status: "not-deliverable", emailableResponse: testResponse, emailableScore: testResponse.score };
|
||||
}
|
||||
// Always reject the explicit test domain, regardless of API key
|
||||
if (emailDomain === EMAILABLE_NOT_DELIVERABLE_TEST_DOMAIN) {
|
||||
const testResponse = buildTestUndeliverableResponse(email);
|
||||
return { status: "not-deliverable", emailableResponse: testResponse, emailableScore: testResponse.score };
|
||||
}
|
||||
|
||||
if (!rawApiKey) {
|
||||
if (["development", "test"].includes(getNodeEnvironment())) {
|
||||
if (!rawApiKey) {
|
||||
if (["development", "test"].includes(getNodeEnvironment())) {
|
||||
return { status: "ok", emailableScore: null };
|
||||
}
|
||||
throw new StackAssertionError("STACK_EMAILABLE_API_KEY must not be empty; set it to 'disable_email_validation' to disable email validation");
|
||||
}
|
||||
|
||||
const apiKey = rawApiKey === "disable_email_validation" ? "" : rawApiKey;
|
||||
if (!apiKey || isReservedTestDomain(emailDomain)) {
|
||||
return { status: "ok", emailableScore: null };
|
||||
}
|
||||
throw new StackAssertionError("STACK_EMAILABLE_API_KEY must not be empty; set it to 'disable_email_validation' to disable email validation");
|
||||
|
||||
const clientFactory = options?._clientFactory ?? createEmailableClient;
|
||||
const retryDelayBase = options?.retryExponentialDelayBaseMs ?? RETRY_BACKOFF_BASE_MS;
|
||||
|
||||
return await traceSpan("checking email address with Emailable", async () => {
|
||||
const client = clientFactory(apiKey);
|
||||
const raw = await verifyWithRetries(() => client.verify(email), 4, retryDelayBase);
|
||||
console.log("Received emailable response", { email, raw });
|
||||
const response = validateVerifyResponse(raw);
|
||||
|
||||
if (response.state === "undeliverable") {
|
||||
return { status: "not-deliverable", emailableResponse: response, emailableScore: response.score };
|
||||
}
|
||||
return { status: "ok", emailableScore: response.score };
|
||||
});
|
||||
} catch (error) {
|
||||
captureError("emailable-api-error", new StackAssertionError("Error while checking email address with Emailable", { cause: error, email, options }));
|
||||
return { status: "error", error, emailableScore: null };
|
||||
}
|
||||
|
||||
const apiKey = rawApiKey === "disable_email_validation" ? "" : rawApiKey;
|
||||
if (!apiKey || isReservedTestDomain(emailDomain)) {
|
||||
return { status: "ok", emailableScore: null };
|
||||
}
|
||||
|
||||
const clientFactory = options?._clientFactory ?? createEmailableClient;
|
||||
const retryDelayBase = options?.retryExponentialDelayBaseMs ?? RETRY_BACKOFF_BASE_MS;
|
||||
|
||||
return await traceSpan("checking email address with Emailable", async () => {
|
||||
const client = clientFactory(apiKey);
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = await verifyWithRetries(() => client.verify(email), 4, retryDelayBase);
|
||||
} catch (error) {
|
||||
captureError("emailable-api-error", error);
|
||||
return { status: "error", error, emailableScore: null };
|
||||
}
|
||||
const response = validateVerifyResponse(raw);
|
||||
|
||||
if (response.state === "undeliverable" || response.disposable) {
|
||||
return { status: "not-deliverable", emailableResponse: response, emailableScore: response.score };
|
||||
}
|
||||
return { status: "ok", emailableScore: response.score };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dashboard",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dev-launchpad",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/e2e-tests",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@stackframe/hosted-components",
|
||||
"private": true,
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/mock-oauth-server",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-docs",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-cjs-test",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/convex-example",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-demo-app",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"description": "",
|
||||
"private": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/docs-examples",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"description": "",
|
||||
"private": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/e-commerce-demo",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/js-example",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"description": "",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@stackframe/lovable-react-18-example",
|
||||
"private": true,
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-middleware-demo",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-supabase",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dashboard-ui-components",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/init-stack",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"description": "The setup wizard for Stack. https://stack-auth.com",
|
||||
"main": "dist/index.mjs",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/js",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/react",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-cli",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"description": "The CLI for Stack Auth. https://stack-auth.com",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-sc",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"exports": {
|
||||
"./force-react-server": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-shared",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsdown",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-ui",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/stack",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
"//": "NEXT_LINE_PLATFORM template",
|
||||
"private": true,
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/template",
|
||||
"private": true,
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"repository": "https://github.com/stack-auth/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/swift-sdk",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"private": true,
|
||||
"description": "Stack Auth Swift SDK",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/sdk-spec",
|
||||
"version": "2.8.77",
|
||||
"version": "2.8.78",
|
||||
"private": true,
|
||||
"description": "Stack Auth SDK specification files",
|
||||
"scripts": {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user