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:
Aditya Tripathi
2025-06-27 21:33:29 +05:30
committed by GitHub
parent 0049644224
commit 00334e501f
10 changed files with 187 additions and 4 deletions

View File

@@ -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

View File

@@ -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: ..

View File

@@ -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

View File

@@ -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

View File

@@ -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",

View File

@@ -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: .

View File

@@ -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:

View 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"]

View File

@@ -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 .",

View File

@@ -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([