mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Merge seed scripts (#354)
This commit is contained in:
parent
7431a3c2fb
commit
d5d28b2dd1
5
.github/workflows/docker-build.yaml
vendored
5
.github/workflows/docker-build.yaml
vendored
@ -24,7 +24,8 @@ jobs:
|
||||
with:
|
||||
images: ${{ secrets.DOCKER_REPO }}/server
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=ref,event=branch,enable=${{ github.ref != 'refs/heads/main' }}
|
||||
type=sha,prefix=
|
||||
type=match,pattern=\d.\d.\d
|
||||
|
||||
@ -56,5 +57,5 @@ jobs:
|
||||
context: .
|
||||
file: ./docker/server/Dockerfile
|
||||
push: ${{ steps.push-condition.outputs.should_push }}
|
||||
tags: ${{ steps.meta.outputs.tags == 'main' && 'latest' || steps.meta.outputs.tags }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
@ -3,6 +3,17 @@ NEXT_PUBLIC_STACK_API_URL=# the base URL of Stack's backend/API. For local devel
|
||||
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`.
|
||||
STACK_SERVER_SECRET=# a random, unguessable secret key generated by `pnpm generate-keys`
|
||||
|
||||
# seed script settings
|
||||
STACK_SEED_SIGN_UP_ENABLED=# true to add OTP auth to the dashboard when seeding
|
||||
STACK_SEED_OTP_ENABLED=# true to add OTP auth to the dashboard when seeding
|
||||
STACK_SEED_ALLOW_LOCALHOST=# true to allow running dashboard on the localhost, set this to true only in development
|
||||
STACK_SEED_OAUTH_PROVIDERS=# list of oauth providers to add to the dashboard when seeding, separated by comma, for example "github,google,facebook"
|
||||
STACK_SEED_CLIENT_TEAM_CREATION=# true to allow the users of the internal project to create teams
|
||||
STACK_SEED_USER_EMAIL=# default user added to the dashboard
|
||||
STACK_SEED_USER_PASSWORD=# default user's password, paired with STACK_SEED_USER_EMAIL
|
||||
STACK_SEED_USER_INTERNAL_ACCESS=# if the default user has access to the internal dashboard project
|
||||
STACK_SEED_USER_GITHUB_ID=# add github oauth id to the default user
|
||||
|
||||
# OAuth mock provider settings
|
||||
STACK_OAUTH_MOCK_URL=# enter the URL of the mock OAuth provider here. For local development, use `http://localhost:8114`.
|
||||
|
||||
|
||||
@ -2,6 +2,18 @@ NEXT_PUBLIC_STACK_API_URL=http://localhost:8102
|
||||
NEXT_PUBLIC_STACK_DASHBOARD_URL=http://localhost:8101
|
||||
STACK_SERVER_SECRET=23-wuNpik0gIW4mruTz25rbIvhuuvZFrLOLtL7J4tyo
|
||||
|
||||
STACK_SEED_SIGN_UP_ENABLED=true
|
||||
STACK_SEED_OTP_ENABLED=true
|
||||
STACK_SEED_ALLOW_LOCALHOST=true
|
||||
STACK_SEED_OAUTH_PROVIDERS=github,spotify,google,microsoft
|
||||
STACK_SEED_CLIENT_TEAM_CREATION=true
|
||||
STACK_SEED_USER_INTERNAL_ACCESS=true
|
||||
|
||||
NEXT_PUBLIC_STACK_PROJECT_ID=project-id-from-stack-dashboard
|
||||
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-local-development-only
|
||||
STACK_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only
|
||||
STACK_SUPER_SECRET_ADMIN_KEY=this-super-secret-admin-key-is-for-local-development-only
|
||||
|
||||
STACK_OAUTH_MOCK_URL=http://localhost:8114
|
||||
|
||||
STACK_GITHUB_CLIENT_ID=MOCK
|
||||
|
||||
@ -1,202 +0,0 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { hashPassword } from "@stackframe/stack-shared/dist/utils/hashes";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function seed() {
|
||||
console.log('Seeding database...');
|
||||
|
||||
// Optional default admin user
|
||||
const adminEmail = process.env.STACK_DEFAULT_DASHBOARD_USER_EMAIL;
|
||||
const adminPassword = process.env.STACK_DEFAULT_DASHBOARD_USER_PASSWORD;
|
||||
const adminInternalAccess = process.env.STACK_DEFAULT_DASHBOARD_USER_INTERNAL_ACCESS === 'true';
|
||||
|
||||
// Optionally disable sign up for "internal" project
|
||||
const signUpEnabled = process.env.STACK_INTERNAL_SIGN_UP_ENABLED === 'true';
|
||||
|
||||
// Optionally add a custom domain to the internal project
|
||||
const dashboardDomain = process.env.NEXT_PUBLIC_STACK_DASHBOARD_URL;
|
||||
const allowLocalhost = process.env.STACK_DASHBOARD_ALLOW_LOCALHOST === 'true';
|
||||
|
||||
let internalProject = await prisma.project.findUnique({
|
||||
where: {
|
||||
id: 'internal',
|
||||
},
|
||||
include: {
|
||||
config: true,
|
||||
}
|
||||
});
|
||||
|
||||
if (!internalProject) {
|
||||
console.log('No existing internal project found, creating...');
|
||||
|
||||
internalProject = await prisma.project.create({
|
||||
data: {
|
||||
id: 'internal',
|
||||
displayName: 'Stack Dashboard',
|
||||
description: 'Stack\'s admin dashboard',
|
||||
isProductionMode: false,
|
||||
apiKeySets: {
|
||||
create: [{
|
||||
description: "Internal API key set",
|
||||
// These keys must match the values used in the Stack dashboard env to be able to login via the UI.
|
||||
publishableClientKey: process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY,
|
||||
secretServerKey: process.env.STACK_SECRET_SERVER_KEY,
|
||||
superSecretAdminKey: process.env.STACK_SUPER_SECRET_ADMIN_KEY,
|
||||
expiresAt: new Date('2099-12-31T23:59:59Z'),
|
||||
}],
|
||||
},
|
||||
config: {
|
||||
create: {
|
||||
allowLocalhost: true,
|
||||
signUpEnabled, // see STACK_SIGN_UP_DISABLED var above
|
||||
emailServiceConfig: {
|
||||
create: {
|
||||
proxiedEmailServiceConfig: {
|
||||
create: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
createTeamOnSignUp: false,
|
||||
clientTeamCreationEnabled: false,
|
||||
authMethodConfigs: {
|
||||
create: [
|
||||
{
|
||||
passwordConfig: {
|
||||
create: {},
|
||||
}
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
include: {
|
||||
config: true,
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Internal project created');
|
||||
}
|
||||
|
||||
// Create optional default admin user if credentials are provided.
|
||||
// This user will be able to login to the dashboard with both email/password and magic link.
|
||||
if (adminEmail && adminPassword) {
|
||||
const oldAdminUser = await prisma.projectUser.findFirst({
|
||||
where: {
|
||||
projectId: 'internal',
|
||||
contactChannels: {
|
||||
some: {
|
||||
type: 'EMAIL',
|
||||
value: adminEmail,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (oldAdminUser) {
|
||||
console.log(`User with email ${adminEmail} already exists, skipping creation`);
|
||||
} else {
|
||||
console.log(`No existing admin user with email ${adminEmail} found, creating...`);
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const newUser = await tx.projectUser.create({
|
||||
data: {
|
||||
projectId: 'internal',
|
||||
serverMetadata: adminInternalAccess
|
||||
? { managedProjectIds: ['internal'] }
|
||||
: undefined,
|
||||
}
|
||||
});
|
||||
|
||||
await tx.contactChannel.create({
|
||||
data: {
|
||||
projectUserId: newUser.projectUserId,
|
||||
projectId: 'internal',
|
||||
type: 'EMAIL' as const,
|
||||
value: adminEmail as string,
|
||||
isVerified: false,
|
||||
isPrimary: 'TRUE',
|
||||
usedForAuth: 'TRUE',
|
||||
}
|
||||
});
|
||||
|
||||
const passwordConfig = await tx.passwordAuthMethodConfig.findFirstOrThrow({
|
||||
where: {
|
||||
projectConfigId: (internalProject as any).configId
|
||||
},
|
||||
include: {
|
||||
authMethodConfig: true,
|
||||
}
|
||||
});
|
||||
|
||||
await tx.authMethod.create({
|
||||
data: {
|
||||
projectId: 'internal',
|
||||
projectConfigId: (internalProject as any).configId,
|
||||
projectUserId: newUser.projectUserId,
|
||||
authMethodConfigId: passwordConfig.authMethodConfigId,
|
||||
passwordAuthMethod: {
|
||||
create: {
|
||||
passwordHash: await hashPassword(adminPassword),
|
||||
projectUserId: newUser.projectUserId,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Initial admin user created: ', adminEmail);
|
||||
}
|
||||
}
|
||||
|
||||
if (internalProject.config.allowLocalhost !== allowLocalhost) {
|
||||
console.log('Updating allowLocalhost for internal project: ', allowLocalhost);
|
||||
|
||||
await prisma.project.update({
|
||||
where: { id: 'internal' },
|
||||
data: {
|
||||
config: {
|
||||
update: {
|
||||
allowLocalhost,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (dashboardDomain) {
|
||||
const url = new URL(dashboardDomain);
|
||||
|
||||
if (url.hostname !== 'localhost') {
|
||||
console.log('Adding trusted domain for internal project: ', dashboardDomain);
|
||||
|
||||
await prisma.projectDomain.upsert({
|
||||
where: {
|
||||
projectConfigId_domain: {
|
||||
projectConfigId: internalProject.configId,
|
||||
domain: dashboardDomain,
|
||||
}
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
projectConfigId: internalProject.configId,
|
||||
domain: dashboardDomain,
|
||||
handlerPath: '/',
|
||||
}
|
||||
});
|
||||
} else if (!allowLocalhost) {
|
||||
throw new Error('Cannot use localhost as a trusted domain if STACK_DASHBOARD_ALLOW_LOCALHOST is not set to true');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Seeding complete!');
|
||||
}
|
||||
|
||||
seed().catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
}).finally(async () => await prisma.$disconnect());
|
||||
@ -1,141 +1,260 @@
|
||||
import { prismaClient } from '@/prisma-client';
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
import { throwErr } from '@stackframe/stack-shared/dist/utils/errors';
|
||||
import { hashPassword } from "@stackframe/stack-shared/dist/utils/hashes";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function seed() {
|
||||
console.log('Seeding database...');
|
||||
|
||||
const oldProject = await prisma.project.findUnique({
|
||||
// Optional default admin user
|
||||
const adminEmail = process.env.STACK_SEED_USER_EMAIL;
|
||||
const adminPassword = process.env.STACK_SEED_USER_PASSWORD;
|
||||
const adminInternalAccess = process.env.STACK_SEED_USER_INTERNAL_ACCESS === 'true';
|
||||
const adminGithubId = process.env.STACK_SEED_USER_GITHUB_ID;
|
||||
|
||||
// dashboard settings
|
||||
const dashboardDomain = process.env.NEXT_PUBLIC_STACK_DASHBOARD_URL;
|
||||
const oauthProviderIds = process.env.STACK_SEED_OAUTH_PROVIDERS?.split(',') ?? [];
|
||||
const otpEnabled = process.env.STACK_SEED_OTP_ENABLED === 'true';
|
||||
const signUpEnabled = process.env.STACK_SEED_SIGN_UP_ENABLED === 'true';
|
||||
const allowLocalhost = process.env.STACK_SEED_ALLOW_LOCALHOST === 'true';
|
||||
const clientTeamCreation = process.env.STACK_SEED_CLIENT_TEAM_CREATION === 'true';
|
||||
|
||||
let internalProject = await prisma.project.findUnique({
|
||||
where: {
|
||||
id: 'internal',
|
||||
},
|
||||
include: {
|
||||
config: true,
|
||||
}
|
||||
});
|
||||
|
||||
if (oldProject) {
|
||||
console.log('Internal project already exists, skipping its creation');
|
||||
} else {
|
||||
await prismaClient.$transaction(async (tx) => {
|
||||
const createdProject = await prisma.project.upsert({
|
||||
where: {
|
||||
id: 'internal',
|
||||
if (!internalProject) {
|
||||
internalProject = await prisma.project.create({
|
||||
data: {
|
||||
id: 'internal',
|
||||
displayName: 'Stack Dashboard',
|
||||
description: 'Stack\'s admin dashboard',
|
||||
isProductionMode: false,
|
||||
apiKeySets: {
|
||||
create: [{
|
||||
description: "Internal API key set",
|
||||
// These keys must match the values used in the Stack dashboard env to be able to login via the UI.
|
||||
publishableClientKey: process.env.NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY || throwErr('NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY is not set'),
|
||||
secretServerKey: process.env.STACK_SECRET_SERVER_KEY || throwErr('STACK_SECRET_SERVER_KEY is not set'),
|
||||
superSecretAdminKey: process.env.STACK_SUPER_SECRET_ADMIN_KEY || throwErr('STACK_SUPER_SECRET_ADMIN_KEY is not set'),
|
||||
expiresAt: new Date('2099-12-31T23:59:59Z'),
|
||||
}],
|
||||
},
|
||||
create: {
|
||||
id: 'internal',
|
||||
displayName: 'Stack Dashboard',
|
||||
description: 'Stack\'s admin dashboard',
|
||||
isProductionMode: false,
|
||||
apiKeySets: {
|
||||
create: [{
|
||||
description: "Internal API key set",
|
||||
publishableClientKey: "this-publishable-client-key-is-for-local-development-only",
|
||||
secretServerKey: "this-secret-server-key-is-for-local-development-only",
|
||||
superSecretAdminKey: "this-super-secret-admin-key-is-for-local-development-only",
|
||||
expiresAt: new Date('2099-12-31T23:59:59Z'),
|
||||
}],
|
||||
},
|
||||
config: {
|
||||
create: {
|
||||
allowLocalhost: true,
|
||||
oauthProviderConfigs: {
|
||||
create: (['github', 'spotify', 'google', 'microsoft'] as const).map((id) => ({
|
||||
id,
|
||||
proxiedOAuthConfig: {
|
||||
create: {
|
||||
type: id.toUpperCase() as any,
|
||||
}
|
||||
},
|
||||
projectUserOAuthAccounts: {
|
||||
create: []
|
||||
}
|
||||
})),
|
||||
},
|
||||
emailServiceConfig: {
|
||||
create: {
|
||||
proxiedEmailServiceConfig: {
|
||||
create: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
createTeamOnSignUp: false,
|
||||
clientTeamCreationEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
|
||||
await prisma.projectConfig.update({
|
||||
where: {
|
||||
id: createdProject.configId,
|
||||
},
|
||||
data: {
|
||||
authMethodConfigs: {
|
||||
create: [
|
||||
{
|
||||
otpConfig: {
|
||||
create: {
|
||||
contactChannelType: 'EMAIL',
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
passwordConfig: {
|
||||
config: {
|
||||
create: {
|
||||
allowLocalhost: true,
|
||||
signUpEnabled,
|
||||
emailServiceConfig: {
|
||||
create: {
|
||||
proxiedEmailServiceConfig: {
|
||||
create: {}
|
||||
}
|
||||
},
|
||||
...(['github', 'spotify', 'google', 'microsoft'] as const).map((id) => ({
|
||||
oauthProviderConfig: {
|
||||
connect: {
|
||||
projectConfigId_id: {
|
||||
id,
|
||||
projectConfigId: createdProject.configId,
|
||||
}
|
||||
}
|
||||
},
|
||||
createTeamOnSignUp: false,
|
||||
clientTeamCreationEnabled: clientTeamCreation,
|
||||
authMethodConfigs: {
|
||||
create: [
|
||||
{
|
||||
passwordConfig: {
|
||||
create: {},
|
||||
}
|
||||
},
|
||||
...(otpEnabled ? [{
|
||||
otpConfig: {
|
||||
create: {
|
||||
contactChannelType: 'EMAIL'
|
||||
},
|
||||
}
|
||||
}]: []),
|
||||
],
|
||||
},
|
||||
oauthProviderConfigs: {
|
||||
create: oauthProviderIds.map((id) => ({
|
||||
id,
|
||||
proxiedOAuthConfig: {
|
||||
create: {
|
||||
type: id.toUpperCase() as any,
|
||||
}
|
||||
},
|
||||
projectUserOAuthAccounts: {
|
||||
create: []
|
||||
}
|
||||
}))
|
||||
],
|
||||
},
|
||||
})),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('Internal project created');
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const adminGithubId = process.env.STACK_SETUP_ADMIN_GITHUB_ID;
|
||||
if (adminGithubId) {
|
||||
console.log("Found admin GitHub ID in environment variables, creating admin user...");
|
||||
await prisma.projectUser.upsert({
|
||||
where: {
|
||||
projectId_projectUserId: {
|
||||
projectId: 'internal',
|
||||
projectUserId: '707156c3-0d1b-48cf-b09d-3171c7f613d5',
|
||||
},
|
||||
},
|
||||
create: {
|
||||
projectId: 'internal',
|
||||
projectUserId: '707156c3-0d1b-48cf-b09d-3171c7f613d5',
|
||||
displayName: 'Admin user generated by seed script',
|
||||
serverMetadata: {
|
||||
managedProjectIds: [
|
||||
"internal",
|
||||
"12345678-1234-1234-1234-123456789abc", // intentionally invalid project ID to ensure we don't rely on project IDs being valid
|
||||
],
|
||||
},
|
||||
projectUserOAuthAccounts: {
|
||||
create: [{
|
||||
providerAccountId: adminGithubId,
|
||||
projectConfigId: createdProject.configId,
|
||||
oauthProviderConfigId: 'github',
|
||||
}],
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
console.log(`Admin user created (if it didn't already exist)`);
|
||||
} else {
|
||||
console.log('No admin GitHub ID found in environment variables, skipping admin user creation');
|
||||
},
|
||||
include: {
|
||||
config: true,
|
||||
}
|
||||
});
|
||||
|
||||
await prisma.projectConfig.update({
|
||||
where: {
|
||||
id: internalProject.configId,
|
||||
},
|
||||
data: {
|
||||
authMethodConfigs: {
|
||||
create: [
|
||||
...oauthProviderIds.map((id) => ({
|
||||
oauthProviderConfig: {
|
||||
connect: {
|
||||
projectConfigId_id: {
|
||||
id,
|
||||
projectConfigId: (internalProject as any).configId,
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
],
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Internal project created');
|
||||
}
|
||||
|
||||
// Create optional default admin user if credentials are provided.
|
||||
// This user will be able to login to the dashboard with both email/password and magic link.
|
||||
if ((adminEmail && adminPassword) || adminGithubId) {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const oldAdminUser = await tx.projectUser.findFirst({
|
||||
where: {
|
||||
projectId: 'internal',
|
||||
projectUserId: '33e7c043-d2d1-4187-acd3-f91b5ed64b46'
|
||||
}
|
||||
});
|
||||
|
||||
if (oldAdminUser) {
|
||||
console.log(`User with email ${adminEmail} already exists, skipping creation`);
|
||||
} else {
|
||||
const newUser = await tx.projectUser.create({
|
||||
data: {
|
||||
projectUserId: '33e7c043-d2d1-4187-acd3-f91b5ed64b46',
|
||||
projectId: 'internal',
|
||||
serverMetadata: adminInternalAccess
|
||||
? { managedProjectIds: ['internal'] }
|
||||
: undefined,
|
||||
}
|
||||
});
|
||||
|
||||
if (adminEmail && adminPassword) {
|
||||
await tx.contactChannel.create({
|
||||
data: {
|
||||
projectUserId: newUser.projectUserId,
|
||||
projectId: 'internal',
|
||||
type: 'EMAIL' as const,
|
||||
value: adminEmail as string,
|
||||
isVerified: false,
|
||||
isPrimary: 'TRUE',
|
||||
usedForAuth: 'TRUE',
|
||||
}
|
||||
});
|
||||
|
||||
const passwordConfig = await tx.passwordAuthMethodConfig.findFirstOrThrow({
|
||||
where: {
|
||||
projectConfigId: (internalProject as any).configId
|
||||
},
|
||||
include: {
|
||||
authMethodConfig: true,
|
||||
}
|
||||
});
|
||||
|
||||
await tx.authMethod.create({
|
||||
data: {
|
||||
projectId: 'internal',
|
||||
projectConfigId: (internalProject as any).configId,
|
||||
projectUserId: newUser.projectUserId,
|
||||
authMethodConfigId: passwordConfig.authMethodConfigId,
|
||||
passwordAuthMethod: {
|
||||
create: {
|
||||
passwordHash: await hashPassword(adminPassword),
|
||||
projectUserId: newUser.projectUserId,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Added admin user with email ${adminEmail}`);
|
||||
}
|
||||
|
||||
if (adminGithubId) {
|
||||
const githubConfig = await tx.oAuthProviderConfig.findUnique({
|
||||
where: {
|
||||
projectConfigId_id: {
|
||||
projectConfigId: (internalProject as any).configId,
|
||||
id: 'github'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!githubConfig) {
|
||||
throw new Error('GitHub OAuth provider config not found');
|
||||
}
|
||||
|
||||
await tx.projectUserOAuthAccount.create({
|
||||
data: {
|
||||
projectId: 'internal',
|
||||
projectConfigId: (internalProject as any).configId,
|
||||
projectUserId: newUser.projectUserId,
|
||||
oauthProviderConfigId: 'github',
|
||||
providerAccountId: adminGithubId
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Added admin user with GitHub ID ${adminGithubId}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (internalProject.config.allowLocalhost !== allowLocalhost) {
|
||||
console.log('Updating allowLocalhost for internal project: ', allowLocalhost);
|
||||
|
||||
await prisma.project.update({
|
||||
where: { id: 'internal' },
|
||||
data: {
|
||||
config: {
|
||||
update: {
|
||||
allowLocalhost,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (dashboardDomain) {
|
||||
const url = new URL(dashboardDomain);
|
||||
|
||||
if (url.hostname !== 'localhost') {
|
||||
console.log('Adding trusted domain for internal project: ', dashboardDomain);
|
||||
|
||||
await prisma.projectDomain.upsert({
|
||||
where: {
|
||||
projectConfigId_domain: {
|
||||
projectConfigId: internalProject.configId,
|
||||
domain: dashboardDomain,
|
||||
}
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
projectConfigId: internalProject.configId,
|
||||
domain: dashboardDomain,
|
||||
handlerPath: '/',
|
||||
}
|
||||
});
|
||||
} else if (!allowLocalhost) {
|
||||
throw new Error('Cannot use localhost as a trusted domain if STACK_SEED_ALLOW_LOCALHOST is not set to true');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Seeding complete!');
|
||||
@ -145,5 +264,5 @@ seed().catch(async (e) => {
|
||||
console.error(e);
|
||||
await prisma.$disconnect();
|
||||
process.exit(1);
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/return-await
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
}).finally(async () => await prisma.$disconnect());
|
||||
|
||||
@ -3,7 +3,7 @@ import { defineConfig } from 'tsup';
|
||||
// tsup config to build the self-hosting seed script so it can be
|
||||
// run in the Docker container with no extra dependencies.
|
||||
export default defineConfig({
|
||||
entry: ['prisma/seed-self-host.ts'],
|
||||
entry: ['prisma/seed.ts'],
|
||||
format: ['cjs'],
|
||||
outDir: 'dist',
|
||||
target: 'node22',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
NEXT_PUBLIC_STACK_API_URL=# https://your-backend-domain.com
|
||||
NEXT_PUBLIC_STACK_DASHBOARD_URL=# https://your-dashboard-domain.com, this will be added as a trusted domain by the seed script
|
||||
STACK_DASHBOARD_ALLOW_LOCALHOST=# if true, the internal dashboard project will allow localhost as a trusted domain. Do not set this to true in production.
|
||||
STACK_SEED_ALLOW_LOCALHOST=# if true, the internal dashboard project will allow localhost as a trusted domain. Do not set this to true in production.
|
||||
|
||||
STACK_DATABASE_CONNECTION_STRING=# postgres connection string with pooler
|
||||
STACK_DIRECT_DATABASE_CONNECTION_STRING=# postgres direct connection string
|
||||
@ -10,10 +10,16 @@ NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=# a secure random string
|
||||
STACK_SECRET_SERVER_KEY=# a secure random string
|
||||
STACK_SERVER_SECRET=# a 32 bytes base64url encoded random string, used for JWT encryption. can be generated with `pnpm generate-keys`
|
||||
|
||||
# set these if you want to use the default admin user
|
||||
STACK_DEFAULT_DASHBOARD_USER_EMAIL=# your admin email
|
||||
STACK_DEFAULT_DASHBOARD_USER_PASSWORD=# your admin password
|
||||
STACK_DEFAULT_DASHBOARD_USER_INTERNAL_ACCESS=# if true, the default admin user will have access to the internal dashboard project
|
||||
# seed script settings
|
||||
STACK_SEED_SIGN_UP_ENABLED=# true to add OTP auth to the dashboard when seeding
|
||||
STACK_SEED_OTP_ENABLED=# true to add OTP auth to the dashboard when seeding
|
||||
STACK_SEED_ALLOW_LOCALHOST=# true to allow running dashboard on the localhost, set this to true only in development
|
||||
STACK_SEED_OAUTH_PROVIDERS=# list of oauth providers to add to the dashboard when seeding, separated by comma, for example "github,google,facebook"
|
||||
STACK_SEED_CLIENT_TEAM_CREATION=# true to allow the users of the internal project to create teams
|
||||
STACK_SEED_USER_EMAIL=# default user added to the dashboard
|
||||
STACK_SEED_USER_PASSWORD=# default user's password, paired with STACK_SEED_USER_EMAIL
|
||||
STACK_SEED_USER_INTERNAL_ACCESS=# if the default user has access to the internal dashboard project
|
||||
STACK_SEED_USER_GITHUB_ID=# add github oauth id to the default user
|
||||
|
||||
# Set these if you want to use any email functionality
|
||||
STACK_EMAIL_HOST=
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
NEXT_PUBLIC_STACK_API_URL=http://localhost:8102
|
||||
NEXT_PUBLIC_STACK_DASHBOARD_URL=http://localhost:8101
|
||||
STACK_DASHBOARD_ALLOW_LOCALHOST=true
|
||||
|
||||
STACK_DATABASE_CONNECTION_STRING=postgres://postgres:password@host.docker.internal:5432/stackframe
|
||||
STACK_DIRECT_DATABASE_CONNECTION_STRING=postgres://postgres:password@host.docker.internal:5432/stackframe
|
||||
@ -10,9 +9,10 @@ NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=this-publishable-client-key-is-for-loca
|
||||
STACK_SECRET_SERVER_KEY=this-secret-server-key-is-for-local-development-only
|
||||
STACK_SERVER_SECRET=23-wuNpik0gIW4mruTz25rbIvhuuvZFrLOLtL7J4tyo
|
||||
|
||||
STACK_DEFAULT_DASHBOARD_USER_EMAIL=admin@email.com
|
||||
STACK_DEFAULT_DASHBOARD_USER_PASSWORD=password
|
||||
STACK_DEFAULT_DASHBOARD_USER_INTERNAL_ACCESS=false
|
||||
STACK_SEED_ALLOW_LOCALHOST=true
|
||||
STACK_SEED_USER_EMAIL=admin@email.com
|
||||
STACK_SEED_USER_PASSWORD=password
|
||||
STACK_SEED_USER_INTERNAL_ACCESS=false
|
||||
|
||||
STACK_RUN_MIGRATIONS=true
|
||||
STACK_RUN_SEED_SCRIPT=true
|
||||
|
||||
@ -70,7 +70,7 @@ RUN npm i -g prisma
|
||||
COPY --from=builder --chown=node:node /app/apps/backend/.next/standalone ./
|
||||
COPY --from=builder --chown=node:node /app/apps/backend/.next/static ./apps/backend/.next/static
|
||||
COPY --from=builder --chown=node:node /app/apps/backend/prisma ./apps/backend/prisma
|
||||
COPY --from=builder --chown=node:node /app/apps/backend/dist/seed-self-host.js ./apps/backend
|
||||
COPY --from=builder --chown=node:node /app/apps/backend/dist/seed.js ./apps/backend
|
||||
|
||||
# Copy built dashboard
|
||||
COPY --from=builder --chown=node:node /app/apps/dashboard/.next/standalone ./
|
||||
|
||||
@ -12,7 +12,7 @@ fi
|
||||
if [ "$STACK_RUN_SEED_SCRIPT" = "true" ]; then
|
||||
echo "Running seed script..."
|
||||
cd apps/backend
|
||||
node seed-self-host.js
|
||||
node seed.js
|
||||
cd ../..
|
||||
else
|
||||
echo "Skipping seed script."
|
||||
|
||||
Loading…
Reference in New Issue
Block a user