mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-08 22:48:14 -05:00
feat(ci): socket realtime image for hosting (#565)
* feat: socket server for self/local deployment * ci: memory limit and redundant dependency install * chore: update readme, devcontainer, cli package * chore: add new dev scripts and update README for full development setup
This commit is contained in:
@@ -38,4 +38,5 @@ WORKDIR /workspace
|
|||||||
|
|
||||||
# Expose the ports we're interested in
|
# Expose the ports we're interested in
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
EXPOSE 3001
|
EXPOSE 3001
|
||||||
|
EXPOSE 3002
|
||||||
@@ -17,6 +17,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
realtime:
|
||||||
|
condition: service_healthy
|
||||||
migrations:
|
migrations:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
ports:
|
ports:
|
||||||
@@ -30,6 +32,29 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
|
realtime:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: .devcontainer/Dockerfile
|
||||||
|
command: sleep infinity
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
|
- DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio
|
||||||
|
- BETTER_AUTH_URL=http://localhost:3000
|
||||||
|
- NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "3002:3002"
|
||||||
|
working_dir: /workspace
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3002']
|
||||||
|
interval: 90s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
migrations:
|
migrations:
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
|
|||||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -16,6 +16,8 @@ jobs:
|
|||||||
image: ghcr.io/simstudioai/simstudio
|
image: ghcr.io/simstudioai/simstudio
|
||||||
- dockerfile: ./docker/db.Dockerfile
|
- dockerfile: ./docker/db.Dockerfile
|
||||||
image: ghcr.io/simstudioai/migrations
|
image: ghcr.io/simstudioai/migrations
|
||||||
|
- dockerfile: ./docker/realtime.Dockerfile
|
||||||
|
image: ghcr.io/simstudioai/realtime
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -86,7 +86,7 @@ docker compose -f docker-compose.prod.yml up -d
|
|||||||
|
|
||||||
1. Open VS Code with the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
1. Open VS Code with the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
||||||
2. Open the project and click "Reopen in Container" when prompted
|
2. Open the project and click "Reopen in Container" when prompted
|
||||||
3. Run `bun run dev` in the terminal or use the `sim-start` alias
|
3. Run `bun run dev:full` in the terminal or use the `sim-start` alias
|
||||||
|
|
||||||
### Option 4: Manual Setup
|
### Option 4: Manual Setup
|
||||||
|
|
||||||
@@ -111,12 +111,26 @@ cp .env.example .env # Configure with required variables (DATABASE_URL, BETTER_
|
|||||||
bunx drizzle-kit push
|
bunx drizzle-kit push
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Start the development server:
|
4. Start the development servers:
|
||||||
|
|
||||||
|
Next.js app:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Start the realtime server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev:sockets
|
||||||
|
```
|
||||||
|
|
||||||
|
Run both together (recommended):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev:full
|
||||||
|
```
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- **Framework**: [Next.js](https://nextjs.org/) (App Router)
|
- **Framework**: [Next.js](https://nextjs.org/) (App Router)
|
||||||
@@ -128,6 +142,7 @@ bun run dev
|
|||||||
- **Flow Editor**: [ReactFlow](https://reactflow.dev/)
|
- **Flow Editor**: [ReactFlow](https://reactflow.dev/)
|
||||||
- **Docs**: [Fumadocs](https://fumadocs.vercel.app/)
|
- **Docs**: [Fumadocs](https://fumadocs.vercel.app/)
|
||||||
- **Monorepo**: [Turborepo](https://turborepo.org/)
|
- **Monorepo**: [Turborepo](https://turborepo.org/)
|
||||||
|
- **Realtime**: [Socket.io](https://socket.io/)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"dev": "next dev --turbo --port 3000",
|
"dev": "next dev --turbo --port 3000",
|
||||||
"dev:classic": "next dev",
|
"dev:classic": "next dev",
|
||||||
"dev:sockets": "bun run socket-server/index.ts",
|
"dev:sockets": "bun run socket-server/index.ts",
|
||||||
"dev:full": "concurrently \"bun run dev\" \"bun run dev:sockets\"",
|
"dev:full": "concurrently -n \"NextJS,Realtime\" -c \"cyan,magenta\" \"bun run dev\" \"bun run dev:sockets\"",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"prepare": "cd ../.. && bun husky",
|
"prepare": "cd ../.. && bun husky",
|
||||||
|
|||||||
@@ -22,11 +22,14 @@ services:
|
|||||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder}
|
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder}
|
||||||
- RESEND_API_KEY=${RESEND_API_KEY:-placeholder}
|
- RESEND_API_KEY=${RESEND_API_KEY:-placeholder}
|
||||||
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
|
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
|
||||||
|
- SOCKET_SERVER_URL=${SOCKET_SERVER_URL:-http://localhost:3002}
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
migrations:
|
migrations:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
|
realtime:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
|
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
|
||||||
interval: 90s
|
interval: 90s
|
||||||
@@ -34,6 +37,32 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
|
realtime:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/realtime.Dockerfile
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
|
||||||
|
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||||
|
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000}
|
||||||
|
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your_auth_secret_here}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '3002:3002'
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 8G
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3002']
|
||||||
|
interval: 90s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
migrations:
|
migrations:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
|||||||
@@ -21,11 +21,14 @@ services:
|
|||||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder}
|
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-placeholder}
|
||||||
- RESEND_API_KEY=${RESEND_API_KEY:-placeholder}
|
- RESEND_API_KEY=${RESEND_API_KEY:-placeholder}
|
||||||
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
|
- OLLAMA_URL=${OLLAMA_URL:-http://localhost:11434}
|
||||||
|
- SOCKET_SERVER_URL=${SOCKET_SERVER_URL:-http://localhost:3002}
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
migrations:
|
migrations:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
|
realtime:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
|
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000']
|
||||||
interval: 90s
|
interval: 90s
|
||||||
@@ -33,6 +36,30 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
|
realtime:
|
||||||
|
image: ghcr.io/simstudioai/realtime:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '3002:3002'
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 4G
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-simstudio}
|
||||||
|
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
|
||||||
|
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000}
|
||||||
|
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your_auth_secret_here}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3002']
|
||||||
|
interval: 90s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
migrations:
|
migrations:
|
||||||
image: ghcr.io/simstudioai/migrations:latest
|
image: ghcr.io/simstudioai/migrations:latest
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
51
docker/realtime.Dockerfile
Normal file
51
docker/realtime.Dockerfile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# ========================================
|
||||||
|
# Base Stage: Alpine Linux with Bun
|
||||||
|
# ========================================
|
||||||
|
FROM oven/bun:alpine AS base
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Dependencies Stage: Install Dependencies
|
||||||
|
# ========================================
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install turbo globally
|
||||||
|
RUN bun install -g turbo
|
||||||
|
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
RUN mkdir -p apps
|
||||||
|
COPY apps/sim/package.json ./apps/sim/package.json
|
||||||
|
|
||||||
|
RUN bun install --omit dev --ignore-scripts
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Builder Stage: Build the Application
|
||||||
|
# ========================================
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# Runner Stage: Run the Socket Server
|
||||||
|
# ========================================
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
# Copy the entire sim app since socket-server has dependencies on other modules
|
||||||
|
COPY --from=builder /app/apps/sim ./apps/sim
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
|
# Expose socket server port (default 3002, but configurable via PORT env var)
|
||||||
|
EXPOSE 3002
|
||||||
|
ENV PORT=3002 \
|
||||||
|
SOCKET_PORT=3002 \
|
||||||
|
HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
# Run the socket server directly
|
||||||
|
CMD ["bun", "apps/sim/socket-server/index.ts"]
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"dev": "turbo run dev",
|
"dev": "turbo run dev",
|
||||||
|
"dev:sockets": "cd apps/sim && bun run dev:sockets",
|
||||||
|
"dev:full": "cd apps/sim && bun run dev:full",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test",
|
||||||
"format": "bunx biome format --write .",
|
"format": "bunx biome format --write .",
|
||||||
"format:check": "bunx biome format .",
|
"format:check": "bunx biome format .",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { Command } from 'commander'
|
|||||||
const NETWORK_NAME = 'simstudio-network'
|
const NETWORK_NAME = 'simstudio-network'
|
||||||
const DB_CONTAINER = 'simstudio-db'
|
const DB_CONTAINER = 'simstudio-db'
|
||||||
const MIGRATIONS_CONTAINER = 'simstudio-migrations'
|
const MIGRATIONS_CONTAINER = 'simstudio-migrations'
|
||||||
|
const REALTIME_CONTAINER = 'simstudio-realtime'
|
||||||
const APP_CONTAINER = 'simstudio-app'
|
const APP_CONTAINER = 'simstudio-app'
|
||||||
const DEFAULT_PORT = '3000'
|
const DEFAULT_PORT = '3000'
|
||||||
|
|
||||||
@@ -78,6 +79,7 @@ async function cleanupExistingContainers(): Promise<void> {
|
|||||||
await stopAndRemoveContainer(APP_CONTAINER)
|
await stopAndRemoveContainer(APP_CONTAINER)
|
||||||
await stopAndRemoveContainer(DB_CONTAINER)
|
await stopAndRemoveContainer(DB_CONTAINER)
|
||||||
await stopAndRemoveContainer(MIGRATIONS_CONTAINER)
|
await stopAndRemoveContainer(MIGRATIONS_CONTAINER)
|
||||||
|
await stopAndRemoveContainer(REALTIME_CONTAINER)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -101,6 +103,7 @@ async function main() {
|
|||||||
if (options.pull) {
|
if (options.pull) {
|
||||||
await pullImage('ghcr.io/simstudioai/simstudio:latest')
|
await pullImage('ghcr.io/simstudioai/simstudio:latest')
|
||||||
await pullImage('ghcr.io/simstudioai/migrations:latest')
|
await pullImage('ghcr.io/simstudioai/migrations:latest')
|
||||||
|
await pullImage('ghcr.io/simstudioai/realtime:latest')
|
||||||
await pullImage('pgvector/pgvector:pg17')
|
await pullImage('pgvector/pgvector:pg17')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +196,34 @@ async function main() {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the realtime server
|
||||||
|
console.log(chalk.blue('🔄 Starting Realtime Server...'))
|
||||||
|
const realtimeSuccess = await runCommand([
|
||||||
|
'docker',
|
||||||
|
'run',
|
||||||
|
'-d',
|
||||||
|
'--name',
|
||||||
|
REALTIME_CONTAINER,
|
||||||
|
'--network',
|
||||||
|
NETWORK_NAME,
|
||||||
|
'-p',
|
||||||
|
'3002:3002',
|
||||||
|
'-e',
|
||||||
|
`DATABASE_URL=postgresql://postgres:postgres@${DB_CONTAINER}:5432/simstudio`,
|
||||||
|
'-e',
|
||||||
|
`BETTER_AUTH_URL=http://localhost:${port}`,
|
||||||
|
'-e',
|
||||||
|
`NEXT_PUBLIC_APP_URL=http://localhost:${port}`,
|
||||||
|
'-e',
|
||||||
|
'BETTER_AUTH_SECRET=your_auth_secret_here',
|
||||||
|
'ghcr.io/simstudioai/realtime:latest',
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!realtimeSuccess) {
|
||||||
|
console.error(chalk.red('❌ Failed to start Realtime Server'))
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the main application
|
// Start the main application
|
||||||
console.log(chalk.blue('🔄 Starting Sim Studio...'))
|
console.log(chalk.blue('🔄 Starting Sim Studio...'))
|
||||||
const appSuccess = await runCommand([
|
const appSuccess = await runCommand([
|
||||||
|
|||||||
Reference in New Issue
Block a user