Skip to content

Commit

Permalink
Split backend and dashboard (stack-auth#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
N2D4 authored Jun 18, 2024
1 parent 02c19ec commit 6480667
Show file tree
Hide file tree
Showing 397 changed files with 5,968 additions and 479 deletions.
48 changes: 17 additions & 31 deletions .github/workflows/e2e-api-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,45 +30,31 @@ jobs:
uses: pnpm/action-setup@v3
with:
version: 9.1.2

- name: Create .env.local file for stack-server
run: |
cat > packages/stack-server/.env.local <<EOF
NEXT_PUBLIC_STACK_URL=http://localhost:8101
NEXT_PUBLIC_STACK_PROJECT_ID=internal
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY=internal-project-client-api-key
STACK_SECRET_SERVER_KEY=internal-project-server-api-key
SERVER_SECRET=23-wuNpik0gIW4mruTz25rbIvhuuvZFrLOLtL7J4tyo

EMAIL_HOST=0.0.0.0
EMAIL_PORT=2500
EMAIL_USERNAME=some-username
EMAIL_PASSWORD=some-password
[email protected]
DATABASE_CONNECTION_STRING=postgres://postgres:password@localhost:5432/stackframe
DIRECT_DATABASE_CONNECTION_STRING=postgres://postgres:password@localhost:5432/stackframe
EOF

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Create .env.local file for stack-backend
run: cp apps/backend/.env.development apps/backend/.env.production.local

- name: Create .env.local file for stack-dashboard
run: cp apps/dashboard/.env.development apps/dashboard/.env.production.local

- name: Build stack-backend
run: pnpm build:backend

- name: Build stack-dashboard
run: pnpm build:dashboard

- name: Start Docker Compose
run: docker-compose -f dependencies.compose.yaml up -d
- name: Initialize database
run: pnpm run prisma:server -- migrate reset --force

- name: Build stack-server
run: pnpm build:server
run: pnpm run prisma -- migrate reset --force

- name: Start stack-server in background
run: pnpm -C packages/stack-server start &
- name: Wait for stack-server to start
- name: Start stack-dashboard in background
run: pnpm run start:dashboard &
- name: Wait for stack-dashboard to start
run: npx [email protected] http://localhost:8101

- name: Run tests
run: pnpm -C apps/e2e test:ci
env:
SERVER_BASE_URL: http://localhost:8101
INTERNAL_PROJECT_ID: internal
INTERNAL_PROJECT_CLIENT_KEY: internal-project-client-api-key
run: pnpm test
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,26 +98,26 @@ pnpm run build
pnpm run codegen

# Push the most recent Prisma schema to the database
pnpm run prisma:server db push
pnpm run prisma db push

# Start the dev server
pnpm run dev
```

You can now open the dashboard at [http://localhost:8101](http://localhost:8101), demo on port 8103, and docs on port 8104.
You can now open the dashboard at [http://localhost:8101](http://localhost:8101), API on port 8102, demo on port 8103, and docs on port 8104. You can also run the tests with `pnpm run test:watch`.

Your IDE may show an error on all `@stackframe/XYZ` imports. To fix this, simply restart the TypeScript language server; for example, in VSCode you can open the command palette (Ctrl+Shift+P) and run `Developer: Reload Window` or `TypeScript: Restart TS server`.

You can also open Prisma Studio to see the database interface and edit data directly:

```sh
pnpm run prisma:server studio
pnpm run prisma studio
```

### Database migrations

If you make changes to the Prisma schema, you need to run the following command to create a migration:

```sh
pnpm run prisma:server migrate dev
pnpm run prisma migrate dev
```
31 changes: 31 additions & 0 deletions apps/backend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Basic
SERVER_SECRET=# enter a secret key generated by `pnpm generate-keys` here. This is used to sign the JWT tokens.

# OAuth shared keys
# Can be omitted if shared OAuth keys are not needed
GITHUB_CLIENT_ID=# client
GITHUB_CLIENT_SECRET=# client secret
GOOGLE_CLIENT_ID=# client id
GOOGLE_CLIENT_SECRET=# client secret
FACEBOOK_CLIENT_ID=# client id
FACEBOOK_CLIENT_SECRET=# client secret
MICROSOFT_CLIENT_ID=# client id
MICROSOFT_CLIENT_SECRET=# client secret
SPOTIFY_CLIENT_ID=# client id
SPOTIFY_CLIENT_SECRET=# client secret

# Email
# For local development, you can spin up a local SMTP server like inbucket
EMAIL_HOST=# for local inbucket: 0.0.0.0
EMAIL_PORT=# for local inbucket: 2500
EMAIL_USERNAME=# for local inbucket: test
EMAIL_PASSWORD=# for local inbucket: none
EMAIL_SENDER=# for local inbucket: [email protected]

# Database
# For local development: `docker run -it --rm -e POSTGRES_PASSWORD=password -p "5432:5432" postgres`
DATABASE_CONNECTION_STRING=# enter your connection string here. For local development: `postgres://postgres:password@localhost:5432/stack`
DIRECT_DATABASE_CONNECTION_STRING=# enter your direct (unpooled or session mode) database connection string here. For local development: same as above

# Misc, optional
STACK_ACCESS_TOKEN_EXPIRATION_TIME=# enter the expiration time for the access token here. Optional, don't specify it for default value
13 changes: 13 additions & 0 deletions apps/backend/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SERVER_SECRET=23-wuNpik0gIW4mruTz25rbIvhuuvZFrLOLtL7J4tyo

DATABASE_CONNECTION_STRING=postgres://postgres:password@localhost:5432/stackframe
DIRECT_DATABASE_CONNECTION_STRING=postgres://postgres:password@localhost:5432/stackframe

NEXT_PUBLIC_DOC_URL=http://localhost:8104

EMAIL_HOST=0.0.0.0
EMAIL_PORT=2500
EMAIL_SECURE=false
EMAIL_USERNAME=does not matter, ignored by Inbucket
EMAIL_PASSWORD=does not matter, ignored by Inbucket
EMAIL_SENDER=[email protected]
5 changes: 5 additions & 0 deletions apps/backend/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
extends: ["../../eslint-configs/defaults.js", "../../eslint-configs/next.js"],
ignorePatterns: ["/*", "!/src", "!/prisma"],
rules: {},
};
File renamed without changes.
File renamed without changes.
95 changes: 95 additions & 0 deletions apps/backend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { withSentryConfig } from "@sentry/nextjs";
import createBundleAnalyzer from "@next/bundle-analyzer";

const withBundleAnalyzer = createBundleAnalyzer({
enabled: !!process.env.ANALYZE_BUNDLE,
});

const withConfiguredSentryConfig = (nextConfig) =>
withSentryConfig(
nextConfig,
{
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options

// Suppresses source map uploading logs during build
silent: true,
org: "stackframe-pw",
project: "stack-api",
},
{
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/

// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,

// Transpiles SDK to be compatible with IE11 (increases bundle size)
transpileClientSDK: true,

// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/monitoring",

// Hides source maps from generated client bundles
hideSourceMaps: true,

// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,

// Enables automatic instrumentation of Vercel Cron Monitors.
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
}
);

/** @type {import('next').NextConfig} */
const nextConfig = {
// we're open-source, so we can provide source maps
productionBrowserSourceMaps: true,
poweredByHeader: false,

async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Cross-Origin-Opener-Policy",
value: "same-origin",
},
{
key: "Permissions-Policy",
value: "",
},
{
key: "Referrer-Policy",
value: "strict-origin-when-cross-origin",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN",
},
{
key: "Content-Security-Policy",
value: "",
},
],
},
];
},
};

export default withConfiguredSentryConfig(
withBundleAnalyzer(
nextConfig
)
);
66 changes: 66 additions & 0 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "@stackframe/stack-backend",
"version": "2.4.26",
"private": true,
"scripts": {
"clean": "rimraf .next && rimraf node_modules",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -c development --",
"with-env:prod": "dotenv -c --",
"dev": "concurrently \"next dev --port 8102\" \"npm run watch-docs\"",
"build": "npm run codegen && next build",
"analyze-bundle": "ANALYZE_BUNDLE=1 npm run build",
"start": "next start --port 8102",
"codegen": "npm run prisma -- generate && npm run generate-docs",
"psql": "npm run with-env -- bash -c 'psql $DATABASE_CONNECTION_STRING'",
"prisma": "npm run with-env -- prisma",
"lint": "next lint",
"watch-docs": "npm run with-env -- chokidar --silent '../../**/*' -i '../../docs/**' -i '../../**/node_modules/**' -i '../../**/.next/**' -i '../../**/dist/**' -c 'tsx scripts/generate-docs.ts'",
"generate-docs": "npm run with-env -- tsx scripts/generate-docs.ts",
"generate-keys": "npm run with-env -- tsx scripts/generate-keys.ts"
},
"prisma": {
"seed": "npm run with-env -- tsx prisma/seed.ts"
},
"dependencies": {
"@hookform/resolvers": "^3.3.4",
"@next/bundle-analyzer": "^14.0.3",
"@node-oauth/oauth2-server": "^5.1.0",
"@prisma/client": "^5.9.1",
"@react-email/components": "^0.0.14",
"@react-email/render": "^0.0.12",
"@react-email/tailwind": "^0.0.14",
"@sentry/nextjs": "^7.105.0",
"@stackframe/stack-shared": "workspace:*",
"@vercel/analytics": "^1.2.2",
"bcrypt": "^5.1.1",
"date-fns": "^3.6.0",
"dotenv-cli": "^7.3.0",
"handlebars": "^4.7.8",
"jose": "^5.2.2",
"lodash": "^4.17.21",
"next": "^14.1",
"nodemailer": "^6.9.10",
"openid-client": "^5.6.4",
"pg": "^8.11.3",
"posthog-js": "^1.138.1",
"prettier": "^3.2.5",
"react": "^18.2",
"react-email": "2.1.0",
"server-only": "^0.0.1",
"sharp": "^0.32.6",
"yaml": "^2.4.5",
"yup": "^1.4.0"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/lodash": "^4.17.4",
"@types/node": "^20.8.10",
"@types/nodemailer": "^6.4.14",
"@types/react": "^18.2.66",
"prisma": "^5.9.1",
"rimraf": "^5.0.5",
"tsx": "^4.7.2",
"glob": "^10.4.1"
}
}
File renamed without changes.
77 changes: 77 additions & 0 deletions apps/backend/prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { PrismaClient } from '@prisma/client';
import { getEnvVariable } from '@stackframe/stack-shared/dist/utils/env';
const prisma = new PrismaClient();


async function seed() {
console.log('Seeding database...');

const oldProjects = await prisma.project.findUnique({
where: {
id: 'internal',
},
});

if (oldProjects) {
console.log('Internal project already exists, skipping seeding');
return;
}

await prisma.project.upsert({
where: {
id: 'internal',
},
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",
expiresAt: new Date('2099-12-31T23:59:59Z'),
}],
},
config: {
create: {
allowLocalhost: true,
oauthProviderConfigs: {
create: (['github', 'facebook', 'google', 'microsoft'] as const).map((id) => ({
id,
proxiedOAuthConfig: {
create: {
type: id.toUpperCase() as any,
}
},
projectUserOAuthAccounts: {
create: []
}
})),
},
emailServiceConfig: {
create: {
proxiedEmailServiceConfig: {
create: {}
}
}
},
credentialEnabled: true,
magicLinkEnabled: true,
createTeamOnSignUp: false,
},
},
},
update: {},
});
console.log('Internal project created');
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, @typescript-eslint/return-await
}).finally(async () => await prisma.$disconnect());
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 6480667

Please sign in to comment.