-
Notifications
You must be signed in to change notification settings - Fork 301
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
356 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
apps/backend/prisma/migrations/20241223225110_fill_empty_project_config_values/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-- Some older versions allowed the empty string for OAuth provider clientId and clientSecret values. | ||
-- We fix that. | ||
|
||
UPDATE "StandardOAuthProviderConfig" SET "clientId" = 'invalid' WHERE "clientId" = ''; | ||
UPDATE "StandardOAuthProviderConfig" SET "clientSecret" = 'invalid' WHERE "clientSecret" = ''; |
4 changes: 4 additions & 0 deletions
4
apps/backend/prisma/migrations/20241223231022_remove_empty_team_profile_images/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-- Some older versions allowed the empty string as a team profile image. | ||
-- We fix that. | ||
|
||
UPDATE "Team" SET "profileImageUrl" = NULL WHERE "profileImageUrl" = ''; |
4 changes: 4 additions & 0 deletions
4
apps/backend/prisma/migrations/20241223231023_onlyhttps_domains/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-- Some older versions allowed http:// URLs as trusted domains, instead of just https://. | ||
-- We fix that. | ||
|
||
UPDATE "ProjectDomain" SET "domain" = 'https://example.com' WHERE "domain" LIKE 'http://%'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { PrismaClient } from "@prisma/client"; | ||
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env"; | ||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors"; | ||
import { filterUndefined } from "@stackframe/stack-shared/dist/utils/objects"; | ||
import { wait } from "@stackframe/stack-shared/dist/utils/promises"; | ||
import { deindent } from "@stackframe/stack-shared/dist/utils/strings"; | ||
|
||
const prismaClient = new PrismaClient(); | ||
|
||
async function main() { | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log("==================================================="); | ||
console.log("Welcome to verify-data-integrity.ts."); | ||
console.log(); | ||
console.log("This script will ensure that the data in the"); | ||
console.log("database is not corrupted."); | ||
console.log(); | ||
console.log("It will call the most important endpoints for"); | ||
console.log("each project and every user, and ensure that"); | ||
console.log("the status codes are what they should be."); | ||
console.log(); | ||
console.log("It's a good idea to run this script on REPLICAS"); | ||
console.log("of the production database regularly (not the actual"); | ||
console.log("prod db!); it should never fail at any point in time."); | ||
console.log(); | ||
console.log(""); | ||
console.log("\x1b[41mIMPORTANT\x1b[0m: This script may modify"); | ||
console.log("the database during its execution in all sorts of"); | ||
console.log("ways, so don't run it on production!"); | ||
console.log(); | ||
console.log("==================================================="); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log("Starting in 3 seconds..."); | ||
await wait(1000); | ||
console.log("2..."); | ||
await wait(1000); | ||
console.log("1..."); | ||
await wait(1000); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
|
||
|
||
const projects = await prismaClient.project.findMany({ | ||
select: { | ||
id: true, | ||
displayName: true, | ||
}, | ||
orderBy: { | ||
id: "asc", | ||
}, | ||
}); | ||
console.log(`Found ${projects.length} projects, iterating over them.`); | ||
|
||
for (let i = 0; i < projects.length; i++) { | ||
const projectId = projects[i].id; | ||
await recurse(`[project ${i + 1}/${projects.length}] ${projectId} ${projects[i].displayName}`, async (recurse) => { | ||
await Promise.all([ | ||
expectStatusCode(200, `/api/v1/projects/current`, { | ||
method: "GET", | ||
headers: { | ||
"x-stack-project-id": projectId, | ||
"x-stack-access-type": "admin", | ||
"x-stack-development-override-key": getEnvVariable("STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY"), | ||
}, | ||
}), | ||
expectStatusCode(200, `/api/v1/users`, { | ||
method: "GET", | ||
headers: { | ||
"x-stack-project-id": projectId, | ||
"x-stack-access-type": "admin", | ||
"x-stack-development-override-key": getEnvVariable("STACK_SEED_INTERNAL_PROJECT_SUPER_SECRET_ADMIN_KEY"), | ||
}, | ||
}), | ||
]); | ||
}); | ||
} | ||
|
||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log(); | ||
console.log("==================================================="); | ||
console.log("All good!"); | ||
console.log(); | ||
console.log("Goodbye."); | ||
console.log("==================================================="); | ||
console.log(); | ||
console.log(); | ||
} | ||
main().catch((...args) => { | ||
console.error(); | ||
console.error(); | ||
console.error(`\x1b[41mERROR\x1b[0m! Could not verify data integrity. See the error message for more details.`); | ||
console.error(...args); | ||
process.exit(1); | ||
}); | ||
|
||
async function expectStatusCode(expectedStatusCode: number, endpoint: string, request: RequestInit) { | ||
const apiUrl = new URL(getEnvVariable("NEXT_PUBLIC_STACK_API_URL")); | ||
const response = await fetch(new URL(endpoint, apiUrl), { | ||
...request, | ||
headers: { | ||
"x-stack-disable-artificial-development-delay": "yes", | ||
"x-stack-development-disable-extended-logging": "yes", | ||
...filterUndefined(request.headers ?? {}), | ||
}, | ||
}); | ||
if (response.status !== expectedStatusCode) { | ||
throw new StackAssertionError(deindent` | ||
Expected status code ${expectedStatusCode} but got ${response.status} for ${endpoint}: | ||
${await response.text()} | ||
`, { request, response }); | ||
} | ||
const json = await response.json(); | ||
return json; | ||
} | ||
|
||
let lastProgress = performance.now() - 9999999999; | ||
|
||
type RecurseFunction = (progressPrefix: string, inner: (recurse: RecurseFunction) => Promise<void>) => Promise<void>; | ||
|
||
const _recurse = async (progressPrefix: string | ((...args: any[]) => void), inner: Parameters<RecurseFunction>[1]): Promise<void> => { | ||
const progressFunc = typeof progressPrefix === "function" ? progressPrefix : (...args: any[]) => { | ||
console.log(`${progressPrefix}`, ...args); | ||
}; | ||
if (performance.now() - lastProgress > 1000) { | ||
progressFunc(); | ||
lastProgress = performance.now(); | ||
} | ||
try { | ||
return await inner( | ||
(progressPrefix, inner) => _recurse( | ||
(...args) => progressFunc(progressPrefix, ...args), | ||
inner, | ||
), | ||
); | ||
} catch (error) { | ||
progressFunc(`\x1b[41mERROR\x1b[0m!`); | ||
throw error; | ||
} | ||
}; | ||
const recurse: RecurseFunction = _recurse; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.